summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml93
-rw-r--r--CMakeLists.txt4
-rw-r--r--INSTALL.md4
-rw-r--r--README.md39
-rwxr-xr-xacprep107
-rw-r--r--appveyor.yml2
-rw-r--r--cmake/FindUtfcpp.cmake3
-rw-r--r--contrib/ledger-completion.bash2
-rw-r--r--default.nix5
-rw-r--r--doc/NEWS77
-rw-r--r--doc/ledger3.texi103
-rw-r--r--src/CMakeLists.txt10
-rw-r--r--src/amount.cc4
-rw-r--r--src/annotate.cc4
-rw-r--r--src/commodity.cc4
-rw-r--r--src/context.h31
-rw-r--r--src/draft.cc2
-rw-r--r--src/error.cc10
-rw-r--r--src/error.h17
-rw-r--r--src/expr.cc14
-rw-r--r--src/format.cc4
-rw-r--r--src/global.h2
-rw-r--r--src/item.cc2
-rw-r--r--src/item.h14
-rw-r--r--src/main.cc19
-rw-r--r--src/mask.cc3
-rw-r--r--src/op.cc4
-rw-r--r--src/op.h22
-rw-r--r--src/output.cc7
-rw-r--r--src/py_post.cc5
-rw-r--r--src/py_value.cc8
-rw-r--r--src/pyinterp.cc16
-rw-r--r--src/query.cc1
-rw-r--r--src/quotes.cc7
-rw-r--r--src/session.cc4
-rw-r--r--src/session.h4
-rw-r--r--src/stream.cc6
-rw-r--r--src/strptime.cc7
-rw-r--r--src/system.hh.in45
-rw-r--r--src/textual.cc88
-rw-r--r--src/times.cc83
-rw-r--r--src/token.cc4
-rw-r--r--src/utils.h10
-rw-r--r--src/value.cc6
-rw-r--r--src/value.h10
-rw-r--r--src/xact.cc8
-rw-r--r--src/xact.h6
-rw-r--r--test/CMakeLists.txt4
-rw-r--r--test/python/JournalTest.py18
-rw-r--r--test/regress/1187_5.test36
-rw-r--r--test/regress/1703.test11
-rw-r--r--test/regress/1722.test12
-rw-r--r--test/regress/1723.test5
-rw-r--r--test/regress/25A099C9.dat (renamed from test/garbage-input.dat)0
-rw-r--r--test/regress/25A099C9.test60
-rw-r--r--test/unit/CMakeLists.txt3
-rwxr-xr-xtools/travis-before_install.sh16
-rwxr-xr-xtools/travis-install.sh22
-rwxr-xr-xtools/update_copyright_year.sh4
59 files changed, 535 insertions, 586 deletions
diff --git a/.travis.yml b/.travis.yml
index 94489bdc..ae2ff727 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,3 @@
-# Since the Travis CI environment http://docs.travis-ci.com/user/ci-environment/
-# provides GNU GCC 4.6, which does not support -std=c++11 GNU GCC 4.8 is installed
-
# NOTE: Please validate this file after editing it using
# Travis WebLint https://lint.travis-ci.org/
# or travis-lint https://github.com/travis-ci/travis-lint
@@ -8,51 +5,22 @@
language: cpp
compiler:
- gcc
- - clang
os:
- linux
- - osx
sudo: false
cache:
apt: true
env:
global:
- # Boost version to use:
- # _MIN is used when building the master branch
- # _MAX is used when building any other branch
- - BOOST_VERSION_MIN="1.49.0"
- - BOOST_VERSION_MAX="1.61.0"
# List of required boost libraries to build
- BOOST_LIBS="date_time,filesystem,iostreams,python,regex,system,test"
- # List of required Homebrew formulae to install
- - BREWS="gmp,mpfr"
# Encrypted COVERITY_SCAN_TOKEN
- secure: "mYNxD1B8WNSvUeKzInehZ7syi2g1jH2ymeSQxoeKKD2duq3pvNWPdZdc4o9MlWQcAqcz58rhFZRIpuEWCnP0LbbJaG+MyuemMn9uAmg9Y4gFpMsBPHuTdf8pO3rDex+tkrr9puEJFgL+QV/TehxO6NDDpx7UdYvJb+4aZD/auYI="
-
-matrix:
- exclude:
- - os: linux
- compiler: clang
- # Compiling ledger on Linux with clang
- # either crashes clang or results in a ledger binary that crashes with SIGSEGV.
- - os: osx
- compiler: gcc
- # On Mac OS X building ledger with GNU GCC 4.8 fails due to
- # undefined symbols, maybe because boost was not being built with g++-4.8.
- # Undefined symbols for architecture x86_64:
- # "boost::re_detail::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::construct_init(boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags)", referenced from:
- # boost::re_detail::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::perl_matcher(char const*, char const*, boost::match_results<char const*, std::allocator<boost::sub_match<char const*> > >&, boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags, char const*) in main.cc.o
- # boost::re_detail::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::perl_matcher(char const*, char const*, boost::match_results<char const*, std::allocator<boost::sub_match<char const*> > >&, boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags, char const*) in global.cc.o
- # "boost::re_detail::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::find()", referenced from:
- # bool boost::regex_search<char const*, char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >(char const*, char const*, boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags) in main.cc.o
- # bool boost::regex_search<char const*, char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >(char const*, char const*, boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags) in global.cc.o
- - os: osx
- compiler: clang
- # On Mac OS X building ledger with clang fails due to
- # dyld: Library not loaded: libboost_python.dylib
- # Referenced from: /Users/travis/build/ledger/ledger/ledger
- # Reason: image not found
+ matrix:
+ # Boost version to build against; an empty string means the
+ # distribution's default.
+ - BOOST_VERSION="1.61.0"
addons:
coverity_scan:
@@ -63,36 +31,39 @@ addons:
build_command: "make"
branch_pattern: coverity
apt:
- sources:
- - ubuntu-toolchain-r-test
- #- boost-latest
packages:
- - gcc-4.8
- - g++-4.8
- libgmp-dev
- libmpfr-dev
- libedit-dev
- #- libboost1.55-dev
- #- libboost-test1.55-dev
- #- libboost-regex1.55-dev
- #- libboost-python1.55-dev
- #- libboost-system1.55-dev
- #- libboost-date-time1.55-dev
- #- libboost-iostreams1.55-dev
- #- libboost-filesystem1.55-dev
- #- libboost-serialization1.55-dev
+ - libboost-dev
+ - libboost-test-dev
+ - libboost-regex-dev
+ - libboost-python-dev
+ - libboost-system-dev
+ - libboost-date-time-dev
+ - libboost-iostreams-dev
+ - libboost-filesystem-dev
+ - libboost-serialization-dev
before_install:
- - if [ "${TRAVIS_BRANCH}" = "master" ]; then export BOOST_VERSION="${BOOST_VERSION_MIN}"; else export BOOST_VERSION="${BOOST_VERSION_MAX}"; fi
- - if [ -n "${BOOST_VERSION}" ]; then export BOOST_ROOT="${TRAVIS_BUILD_DIR}/../boost-trunk"; export CMAKE_MODULE_PATH="${BOOST_ROOT}"; fi
- - if [ "${CXX}" = "g++" ]; then export CXX="$(which g++-4.8)"; export CC="$(which gcc-4.8)"; fi
- - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then export DYLD_LIBRARY_PATH="${BOOST_ROOT}/lib"; fi
- # c++ is a symlink to clang++, but the compiler behaves differently when invoked as c++
- - if [ "${TRAVIS_OS_NAME}" = "osx" -a "${CXX}" = "clang++" ]; then export CXX="$(which c++)"; export CC="$(which cc)"; fi
- - tools/travis-before_install.sh
+ - |
+ if [ -n "${BOOST_VERSION}" ]; then
+ BOOST_SOURCE="$(mktemp -d)"
+ BOOST_SOURCE_URL="https://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION}/boost_${BOOST_VERSION//./_}.tar.bz2/download"
+ curl -Ls "$BOOST_SOURCE_URL" |
+ tar jx -C "${BOOST_SOURCE}" --strip-components 1
+ fi
install:
- - tools/travis-install.sh
+ - |
+ if [ -n "${BOOST_VERSION}" ]; then
+ export BOOST_ROOT="${HOME}/boost"
+ pushd "${BOOST_SOURCE}"
+ ./bootstrap.sh --with-libraries="${BOOST_LIBS}"
+ ./b2 threading=multi -d0 --prefix="${BOOST_ROOT}" install
+ popd
+ rm -Rf "${BOOST_SOURCE}"
+ fi
before_script:
- cmake . -DUSE_PYTHON=ON -DBUILD_DEBUG=ON
@@ -102,12 +73,6 @@ script:
- ctest --output-on-failure
- PYTHONPATH=. python python/demo.py
-after_script:
- # These scripts are run for informational purposes and
- # should be reintegrated into CTest once they reliably verify the documentation.
- - python test/CheckTexinfo.py -l ledger -s .
- - python test/CheckManpage.py -l ledger -s .
-
notifications:
email:
on_success: change
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8dbd892..c85a5157 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -96,8 +96,6 @@ include(CheckCXXSourceCompiles)
include(CheckCXXSourceRuns)
include(CMakePushCheckState)
-check_function_exists(access HAVE_ACCESS)
-check_function_exists(realpath HAVE_REALPATH)
check_function_exists(getpwuid HAVE_GETPWUID)
check_function_exists(getpwnam HAVE_GETPWNAM)
check_function_exists(ioctl HAVE_IOCTL)
@@ -260,7 +258,7 @@ macro(add_ledger_library_dependencies _target)
endif()
if (HAVE_BOOST_PYTHON)
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
- # Don't link directly to a Python framework on OS X, to avoid segfaults
+ # Don't link directly to a Python framework on macOS, to avoid segfaults
# when the module is imported from a different interpreter
target_link_libraries(${_target} ${Boost_LIBRARIES})
set_target_properties(${_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
diff --git a/INSTALL.md b/INSTALL.md
index 23d0566b..1d5bedba 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -57,7 +57,7 @@ A: You're probably missing some dependency libraries. If you tried
libboost-date-time-dev libboost-filesystem-dev \
libboost-graph-dev libboost-iostreams-dev \
libboost-python-dev libboost-regex-dev libboost-test-dev \
- doxygen libedit-dev libmpc-dev
+ doxygen libedit-dev libmpc-dev tzdata
----------------------------------------------------------------------
@@ -115,7 +115,7 @@ Q: Whenever I try to use the Python support, I get a segfault
A: Make sure that the boost_python library you linked against is using the
exact same Python as the Ledger executable. In particular I see this
- bug on OS X systems where boost_python is linked against the default
+ bug on macOS systems where boost_python is linked against the default
Python, while Ledger is linked against the version provided by MacPorts.
Or vice versa.
diff --git a/README.md b/README.md
index 0aa634b4..de083f7e 100644
--- a/README.md
+++ b/README.md
@@ -84,19 +84,9 @@ Dependency | Version (or greater)
[lcov] | 1.6 _optional_, for `make report`, used with `/./acprep gcov`
[sloccount] | 2.26 _optional_, for `make sloc`
-And for building the outdated `release/2.6.3` branch:
+### macOS
-Dependency | Version
------------|--------
-[GMP] | 4.2.2
-[pcre] | 7.7
-[libofx] | 0.8.3 _optional_
-[expat] | 2.0.1 _optional_
-[libxml2] | 2.7.2 _optional_
-
-### Mac OS X
-
-You can use [Homebrew] or [MacPorts] to install Ledger easily on OS X.
+You can use [Homebrew] or [MacPorts] to install Ledger easily on macOS.
#### 1. Homebrew
@@ -111,7 +101,7 @@ If you to want to startup python, use the following command:
#### 2. MacPorts
-If you build stuff using MacPorts on OS X, as I do, here is what you would
+If you build stuff using MacPorts on macOS, as I do, here is what you would
run:
$ sudo port install -f cmake python26 \
@@ -124,30 +114,19 @@ run:
### Ubuntu
If you're going to build on Ubuntu, `sudo apt-get install ...` the
-following packages (current as of Ubuntu 14.04):
+following packages (current as of Ubuntu 18.04):
$ sudo apt-get install build-essential cmake doxygen \
libboost-system-dev libboost-dev python-dev gettext git \
libboost-date-time-dev libboost-filesystem-dev \
libboost-iostreams-dev libboost-python-dev libboost-regex-dev \
- libboost-test-dev libedit-dev libgmp3-dev libmpfr-dev texinfo
-
-Or, for Ubuntu 12.04:
-
- $ sudo apt-get install build-essential cmake zlib1g-dev libbz2-dev \
- python-dev gettext libgmp3-dev libmpfr-dev libboost-dev \
- libboost-regex-dev libboost-date-time-dev \
- libboost-filesystem-dev libboost-python-dev texinfo lcov \
- sloccount libboost-iostreams-dev libboost-test-dev
+ libboost-test-dev libedit-dev libgmp3-dev libmpfr-dev texinfo tzdata
### Debian
-Debian squeeze (6.0): the version of boost in squeeze is too old
-for ledger and unfortunately no backport is available at the moment.
-
-Debian 7 (wheezy), Debian 8 (jessie), Debian 9 (stretch), Debian testing
-and Debian unstable (sid) contain all components needed to build ledger.
-You can install all required build dependencies using the following command:
+Debian 9 (stretch), Debian 10 (buster), Debian testing and Debian unstable
+(sid) contain all components needed to build ledger. You can install all
+required build dependencies using the following command:
$ sudo apt-get install build-essential cmake autopoint texinfo python-dev \
zlib1g-dev libbz2-dev libgmp3-dev gettext libmpfr-dev \
@@ -166,7 +145,7 @@ footwork for you:
# $HOME/local and build with 2 processes in parallel
$ ./acprep update --boost-suffix=-mt --prefix=$HOME/local -j2
-Please read the contents of `CMakeFiles/CMakeOutput.log` and
+Please read the contents of `CMakeFiles/CMakeOutput.log` and
`CMakeFiles/CMakeError.log` if the configure step fails. Also,
see the `help` subcommand to `acprep`, which explains some of its many
options. It's pretty much the only command I run for configuring, building
diff --git a/acprep b/acprep
index 8d9d67f7..c5b4c203 100755
--- a/acprep
+++ b/acprep
@@ -53,16 +53,17 @@ def which(program):
class BoostInfo(object):
def dependencies(self, system):
- if system == 'darwin-homebrew':
+ if system in ['darwin-homebrew']:
return [ 'boost' ]
- if system == 'darwin-macports':
+ if system in ['darwin-macports']:
return [ 'boost-jam', 'boost', '+python27+universal' ]
- if system == 'centos':
+ if system in ['centos']:
return [ 'boost-devel' ]
- elif system == 'ubuntu-xenial':
+ elif system in ['ubuntu-bionic', 'ubuntu-xenial',
+ 'ubuntu-trusty', 'ubuntu-cosmic']:
return [ 'libboost-dev',
'libboost-date-time-dev',
'libboost-filesystem-dev',
@@ -70,19 +71,10 @@ class BoostInfo(object):
'libboost-python-dev',
'libboost-regex-dev',
'libboost-system-dev',
- 'libboost-test-dev' ]
-
- elif system == 'ubuntu-trusty':
- return [ 'libboost-dev',
- 'libboost-date-time-dev',
- 'libboost-filesystem-dev',
- 'libboost-iostreams-dev',
- 'libboost-python-dev',
- 'libboost-regex-dev',
- 'libboost-system-dev',
- 'libboost-test-dev' ]
+ 'libboost-test-dev',
+ 'tzdata' ]
- elif system == 'ubuntu-saucy' or system == 'ubuntu-precise':
+ elif system in [ 'ubuntu-saucy', 'ubuntu-precise']:
return [ 'autopoint',
'libboost-dev',
'libboost-test-dev',
@@ -92,7 +84,7 @@ class BoostInfo(object):
'libboost-iostreams-dev',
'libboost-python-dev' ]
- elif system == 'ubuntu-lucid':
+ elif system in ['ubuntu-lucid']:
return [ 'bjam', 'autopoint',
'libboost-dev',
'libboost-regex-dev',
@@ -510,11 +502,11 @@ class PrepareBuild(CommandLineApp):
self.log.info("Installing Ledger's build dependencies ...")
- system = self.get_stdout('uname', '-s')
+ system = self.get_stdout('uname', '-s').decode('utf8')
if system == 'Darwin':
if exists('/opt/local/bin/port'):
- self.log.info('Looks like you are using MacPorts on OS X')
+ self.log.info('Looks like you are using MacPorts on macOS')
packages = [
'sudo', 'port', 'install', '-f',
'automake', 'autoconf', 'libtool',
@@ -531,7 +523,7 @@ class PrepareBuild(CommandLineApp):
self.log.info('Executing: ' + ' '.join(packages))
self.execute(*packages)
elif exists('/usr/local/bin/brew') or exists('/opt/local/bin/brew'):
- self.log.info('Looks like you are using Homebrew on OS X')
+ self.log.info('Looks like you are using Homebrew on macOS')
packages = [
'brew', 'install',
'cmake', 'ninja',
@@ -540,7 +532,7 @@ class PrepareBuild(CommandLineApp):
self.log.info('Executing: ' + ' '.join(packages))
self.execute(*packages)
elif exists('/sw/bin/fink'):
- self.log.info('Looks like you are using Fink on OS X')
+ self.log.info('Looks like you are using Fink on macOS')
self.log.error("I don't know the package names for Fink yet!")
sys.exit(1)
@@ -548,14 +540,17 @@ class PrepareBuild(CommandLineApp):
if exists('/etc/issue'):
issue = open('/etc/issue')
if issue.readline().startswith('Ubuntu'):
- release = open('/etc/lsb-release')
- info = release.read()
- release.close()
- if re.search('trusty', info):
- self.log.info('Looks like you are using APT on Ubuntu Trusty')
- packages = [
- 'sudo', 'apt-get', 'install',
- 'build-essential',
+ info = dict([line.strip().split('=', 1)
+ for line in open('/etc/lsb-release')])
+ release = info['DISTRIB_CODENAME']
+ self.log.info('Looks like you are using APT on Ubuntu ' + release)
+ packages = [
+ 'sudo', 'apt-get', 'install',
+ 'build-essential',
+ ]
+
+ if release == 'bionic':
+ packages.extend([
'doxygen',
'cmake',
'ninja-build',
@@ -570,12 +565,9 @@ class PrepareBuild(CommandLineApp):
'lcov',
'libutfcpp-dev',
'sloccount'
- ] + BoostInfo().dependencies('ubuntu-trusty')
- elif re.search('xenial', info):
- self.log.info('Looks like you are using APT on Ubuntu Xenial')
- packages = [
- 'sudo', 'apt-get', 'install',
- 'build-essential',
+ ])
+ elif release == 'trusty':
+ packages.extend([
'doxygen',
'cmake',
'ninja-build',
@@ -590,13 +582,27 @@ class PrepareBuild(CommandLineApp):
'lcov',
'libutfcpp-dev',
'sloccount'
- ] + BoostInfo().dependencies('ubuntu-xenial')
- elif re.search('saucy', info):
- self.log.info('Looks like you are using APT on Ubuntu Saucy')
- packages = [
- 'sudo', 'apt-get', 'install',
- 'build-essential',
- 'libtool',
+ ])
+ elif release == 'xenial':
+ packages.extend([
+ 'doxygen',
+ 'cmake',
+ 'ninja-build',
+ 'zlib1g-dev',
+ 'libbz2-dev',
+ 'python-dev',
+ 'libgmp3-dev',
+ 'libmpfr-dev',
+ 'gettext',
+ 'libedit-dev',
+ 'texinfo',
+ 'lcov',
+ 'libutfcpp-dev',
+ 'sloccount'
+ ])
+ elif release == 'saucy':
+ packages.extend([
+ 'doxygen',
'cmake',
'ninja-build',
'zlib1g-dev',
@@ -609,12 +615,9 @@ class PrepareBuild(CommandLineApp):
'texinfo',
'lcov',
'sloccount'
- ] + BoostInfo().dependencies('ubuntu-saucy')
- elif re.search('precise', info):
- self.log.info('Looks like you are using APT on Ubuntu Precise')
- packages = [
- 'sudo', 'apt-get', 'install',
- 'build-essential',
+ ])
+ elif release == 'precise':
+ packages.extend([
'libtool',
'cmake',
'zlib1g-dev',
@@ -628,11 +631,13 @@ class PrepareBuild(CommandLineApp):
'lcov',
'libutfcpp-dev',
'sloccount'
- ] + BoostInfo().dependencies('ubuntu-precise')
+ ])
else:
self.log.info('I do not recognize your version of Ubuntu!')
packages = None
if packages:
+ packages.extend(
+ BoostInfo().dependencies('ubuntu-' + release))
self.log.info('Executing: ' + ' '.join(packages))
self.execute(*packages)
@@ -692,7 +697,7 @@ class PrepareBuild(CommandLineApp):
elif system.startswith('CYGWIN'):
self.log.info('Looks like you are using Cygwin')
- self.log.info('Please install the dependencies manually.')
+ self.log.info('Please install the dependencies manually.')
#########################################################################
# Determine the system's basic configuration #
@@ -1086,7 +1091,7 @@ being 'debug':
debug Debugging and --verify support (default)
opt Full optimizations
gcov Coverage analysis
- gprof Code profiling (for OS X, just use: 'shark -i ledger ...')
+ gprof Code profiling (for macOS, just use: 'shark -i ledger ...')
Next is the optional build PHASE, with 'config' being the default:
diff --git a/appveyor.yml b/appveyor.yml
index 21fec71c..3b2e7267 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -46,5 +46,5 @@ artifacts:
test_script:
- C:\msys64\usr\bin\bash -lc "export MINGW_PREFIX=C:/msys64/mingw32/ CTEST_OUTPUT_ON_FAILURE=1
- PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER &&
+ PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER &&
make test || echo Errors from tests were ignored"
diff --git a/cmake/FindUtfcpp.cmake b/cmake/FindUtfcpp.cmake
index 185a8d88..93828ef4 100644
--- a/cmake/FindUtfcpp.cmake
+++ b/cmake/FindUtfcpp.cmake
@@ -10,7 +10,8 @@ set(UTFCPP_FOUND FALSE)
find_path(UTFCPP_INCLUDE_DIR
NAMES utf8.h
- HINTS "${UTFCPP_PATH}" "${PROJECT_SOURCE_DIR}/lib/utfcpp/v2_0/source"
+ HINTS "${UTFCPP_PATH}"
+ PATHS "${PROJECT_SOURCE_DIR}/lib/utfcpp/v2_0/source"
)
if (UTFCPP_INCLUDE_DIR)
diff --git a/contrib/ledger-completion.bash b/contrib/ledger-completion.bash
index d46b712d..2da44274 100644
--- a/contrib/ledger-completion.bash
+++ b/contrib/ledger-completion.bash
@@ -59,7 +59,7 @@ _ledger()
accounts="Assets Liabilities Equity Revenue Expenses"
case $prev in
- --@(cache|file|init-file|output|pager|price-db|script))
+ --@(cache|file|init-file|output|pager|price-db|script)|-@(f|i|o))
_filedir
return 0
;;
diff --git a/default.nix b/default.nix
index 85932da9..a60e607f 100644
--- a/default.nix
+++ b/default.nix
@@ -26,6 +26,11 @@ stdenv.mkDerivation {
cmakeFlags = [ "-DCMAKE_INSTALL_LIBDIR=lib" ];
+ buildPhase = "make -j$NIX_BUILD_CORES";
+ checkPhase = "ctest -j$NIX_BUILD_CORES";
+
+ doCheck = true;
+
meta = {
homepage = "http://ledger-cli.org/";
description = "A double-entry accounting system with a command-line reporting interface";
diff --git a/doc/NEWS b/doc/NEWS
index 0cb4b2b9..5152ad7c 100644
--- a/doc/NEWS
+++ b/doc/NEWS
@@ -1,33 +1,72 @@
Ledger NEWS
-* 3.1.2
+* 3.1.2 (unreleased)
-- Increased maximum length for regex from 255 to 4095 (bug #981).
+- Increase maximum length for regex from 255 to 4095 (bug #981)
- Initialize periods from from/since clause rather than earliest
- transaction date (bug #1159).
+ transaction date (bug #1159)
-- Check balance assertions against the amount after the posting (bug #1147).
+- Check balance assertions against the amount after the posting (bug #1147)
-- Allow balance assertions with multiple posts to same account (bug #1187).
+- Allow balance assertions with multiple posts to same account (bug #1187)
-- Fixed period duration of "every X days" and similar statements (bug #370).
+- Fix period duration of "every X days" and similar statements (bug #370)
-- Option --force-color does not require --color anymore (bug #1109).
+- Make option --force-color not require --color anymore (bug #1109)
-- Added quoted_rfc4180 to allow CVS output with RFC 4180 compliant quoting.
+- Add quoted_rfc4180 to allow CVS output with RFC 4180 compliant quoting.
+
+- Add support for --prepend-format in accounts command
+
+- Fix handling of edge cases in trim function (bug #520)
+
+- Fix auto xact posts not getting applied to account total during
+ journal parse (bug #552)
+
+- Transfer null_post flags to generated postings
+
+- Fix segfault when using --market with --group-by
+
+- Use amount_width variable for budget report
+
+- Keep pending items in budgets until the last day they apply
+
+- Fixed bug where .total used in value expressions breaks totals
+
+- Make automated transactions work with assertions (bug #1127)
+
+- Improve parsing of date tokens (bug #1626)
+
+- Don't attempt to invert a value if it's already zero (bug #1703)
+
+- Do not parse user-specified init-file twice
+
+- Fix parsing issue of effective dates (bug #1722, TALOS-2017-0303,
+ CVE-2017-2807)
+
+- Fix use-after-free issue with deferred postings (bug #1723, TALOS-2017-0304,
+ CVE-2017-2808)
- Python: Removed double quotes from Unicode values.
+- Python: Ensure that parse errors produce useful RuntimeErrors
+
+- Python: Expose journal expand_aliases
+
+- Python: Expose journal_t::register_account
+
+- Improve bash completion
+
- Emacs Lisp files have been moved to https://github.com/ledger/ledger-mode
-- Fixed build under MSYS (32-bit).
+- Fix build under MSYS (32-bit).
-- Fixed build under Cygwin.
+- Fix build under Cygwin.
- Various documentation improvements
-* 3.1.1
+* 3.1.1 (2016-01-11)
- Added a --no-revalued option
@@ -57,7 +96,7 @@
- Add continuous integration (https://travis-ci.org/ledger/ledger)
-* 3.1
+* 3.1 (2014-10-05)
- Changed the definition of cost basis to preserve the original cost basis
when a gain or loss is made (if you bought 1 AAA for $10 and then sold
@@ -154,7 +193,7 @@ features, please see the manual.
2008/07/27 Starting fresh
Assets:Checking $750.00
Equity:Opening Balances
-
+
2008/07/27 Starting fresh
Assets:Checking = $1,000.00
Equity:Adjustments
@@ -164,7 +203,7 @@ features, please see the manual.
2008/07/27 Starting fresh
Assets:Checking $750.00
Equity:Opening Balances
-
+
2008/07/27 Starting fresh
Assets:Checking $250.00
Equity:Adjustments
@@ -183,7 +222,7 @@ features, please see the manual.
2008/07/24 Opening Balance
Assets:Checking = $250.00 ; we force set it
Equity:Opening Balances
-
+
2008/07/24 Opening Balance
Assets:Checking = EC 250.00 ; we force set it again
Equity:Opening Balances
@@ -195,7 +234,7 @@ features, please see the manual.
Assets:Checking = $250.00 ; we force set it again
Assets:Checking EC 100.00 ; and add some EC's
Equity:Opening Balances
-
+
2008/07/24 Opening Balance
Assets:Checking = EC 250.00 ; we force set the EC's
Equity:Opening Balances
@@ -213,15 +252,15 @@ features, please see the manual.
2008/07/24 Opening Balance
Assets:Checking = $100.00
Equity:Opening Balances
-
+
2008/07/30 We spend money, with a known balance afterward
Expenses:Food $20.00
Assets:Checking = $80.00
-
+
2008/07/30 Again we spend money, but this time with all the info
Expenses:Food $20.00
Assets:Checking $-20.00 = $60.00
-
+
2008/07/30 This entry yield an 'unbalanced' error
Expenses:Food $20.00
Assets:Checking $-20.00 = $30.00
diff --git a/doc/ledger3.texi b/doc/ledger3.texi
index 0248844c..9f9a1095 100644
--- a/doc/ledger3.texi
+++ b/doc/ledger3.texi
@@ -427,7 +427,7 @@ accounting problems.
Once such tutorial, specifically designed for non-profit charities that seek
to use Ledger, can be found at
-@url{https://k.sfconservancy.org/npo-ledger-cli} (with a copy on GitHub also
+@url{https://k.sfconservancy.org/NPO-Accounting/npo-ledger-cli} (with a copy on GitHub also
available at @url{https://github.com/conservancy/npo-ledger-cli/}). If
you're looking for information about how to use Ledger's tagging system to
handle invoicing, track expenses by program targets, and other such concepts,
@@ -1384,10 +1384,11 @@ account:
(Funds:School) $-100.00
@end smallexample
-When reports are generated, by default they'll appear in terms of the
-funds. In this case, you will likely want to mask out your
-@samp{Assets} account, because otherwise the balance won't make much
-sense:
+The use of round brackets creates a virtual posting without ensuring
+a balance to zero. When reports are generated, by default they'll
+appear in terms of the funds. In this case, you will likely want to
+mask out your @samp{Assets} account, because otherwise the balance
+won't make much sense:
@smallexample @c command:396F24E
$ ledger --no-total bal not ^Assets
@@ -2484,7 +2485,9 @@ Closes block commands like @code{apply} or @code{comment}.
@item include
@findex include
@c instance_t::include_directive in textual.cc
-Include the stated file as if it were part of the current file.
+Include the stated file as if it were part of the current file. The file
+name can contain a wildcard (@samp{*}) to refer to multiple files (e.g.
+@samp{bank/*.ledger}).
@item payee
@findex payee
@@ -2910,7 +2913,7 @@ you a place to put those codes:
A transaction can have a ``state'': cleared, pending, or uncleared. The
default is uncleared. To mark a transaction cleared, put an asterisk
-@samp{*} before the payee, after the date or code:
+@samp{*} after the date, before the code or payee:
@smallexample @c input:validate
2012-03-10 * KFC
@@ -3101,6 +3104,13 @@ it appears as:
This shows that they are all in the same transaction (which is why the
date is not repeated), but they have different payees now.
+If using the @option{--strict} or @option{--pedantic} options, you must
+declare this tag to avoid warnings and errors.
+
+The payee name used with the tag is not enforced by the
+@option{--check-payees} option, due to a bug:
+@url{https://github.com/ledger/ledger/issues/556}.
+
@node Metadata values, Typed metadata, Metadata tags, Metadata
@subsection Metadata values
@@ -3185,9 +3195,9 @@ look ``harder'') rather than parentheses:
@node Expression amounts, Balance verification, Virtual postings, Transactions
@section Expression amounts
-An amount is usually a numerical figure with an (optional) commodity,
-but it can also be any value expression. To indicate this, surround
-the amount expression with parentheses:
+An amount is a numerical figure with a commodity, but it can also be
+any value expression. To indicate this, surround the amount
+expression with parentheses:
@smallexample @c input:validate
2012-03-10 * KFC
@@ -3228,6 +3238,45 @@ A balance assertion has this general form:
This simply asserts that after subtracting $20.00 from Assets:Cash,
that the resulting total matches $500.00. If not, it is an error.
+The assertion has an effect only on the specified commodity. If an account has
+multiple commodities, then only the one asserted is verified:
+
+@smallexample
+2012-03-10 KFC New York
+ Expenses:Food $20.00
+ Assets:Cash $-20.00 = $500.00
+
+2012-03-11 KFC Montreal
+ Expenses:Food 15.00 CAD
+ Assets:Cash -15.00 CAD = $500.00
+@end smallexample
+
+In this case, the amount in USD of cash (which has not changed) is validated.
+Nothing is asserted about the current amount of Canadian dollars in @samp{Asset:Cash}.
+
+@subsubsection Special assertion value 0
+
+The only value that can be asserted without a commodity is @samp{0}.
+This results in a cross-commodities assertion, which makes it possible to
+assert that an account is totally empty.
+
+@smallexample
+2012-03-09 Fill Wallet
+ Revenue $20.00
+ Revenue 15.00 CAD
+ Assets:Cash
+
+2012-03-10 KFC New York
+ Expenses:Food $20.00
+ Assets:Cash $-20.00
+
+2012-03-11 KFC Montreal
+ Expenses:Food 15.00 CAD
+ Assets:Cash -15.00 CAD = 0
+@end smallexample
+
+The last transaction will assert that we are out of cash of any sort.
+
@node Balance assignments, Resetting a balance, Balance assertions, Balance verification
@subsection Balance assignments
@@ -3504,12 +3553,12 @@ Plus, it comes with dangers. This works fine:
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} @@@@ $375.00
Income:Capital Gains $-125.00
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -3523,12 +3572,12 @@ But this does not do what you might expect:
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@@@ $375.00
Income:Capital Gains $-125.00
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -3604,7 +3653,7 @@ expressions):
@smallexample
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -3621,7 +3670,7 @@ indicate a virtual cost:
@smallexample
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] (Oh my!) @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] (Oh my!) @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -8087,8 +8136,13 @@ posting.
A regular expression that matches against a transaction's payee name.
@item %/REGEX/
-@itemx tag(REGEX)
-A regular expression that matches against a transaction's tags.
+@itemx expr has_tag(/REGEX/)
+@itemx expr has_tag('TAG')
+A regular expression (REGEX) or string (TAG) that checks for the tags of
+a transaction.
+
+@item tag(REGEX) =~ /REGEX/
+A regular expression that matches a transaction's tags against its values.
@item expr date =~ /REGEX/
Useful for specifying a date in plain terms. For example, you could say
@@ -8157,6 +8211,19 @@ posting.
A regular expression that matches against the transaction code (the
text that occurs between parentheses before the payee).
+@item expr any(KEYWORD =~ /REGEX/)
+The @command{any} keyword is used to specify that at least one posting of
+the transaction must match the expression in brackets. For example,
+@samp{ledger -f d reg expr "any(account =~ /Assets:/)"} can be used to
+display all transactions which involve at least one @samp{Assets:}
+account.
+
+@item expr all(KEYWORD =~ /REGEX/)
+The @command{all} keyword is used to specify that all postings of a
+transactions must match the expression in brackets. For example,
+@samp{ledger -f d reg expr "all(account =~ /Assets:/)"} can be used to
+display all transactions where all accounts are @samp{Assets:}.
+
@end table
The @command{query} command can be used to see how Ledger interprets
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 71d9478a..9cd54dbe 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -52,7 +52,6 @@ set(LEDGER_SOURCES
times.cc
error.cc
utils.cc
- strptime.cc
wcwidth.cc)
if (HAVE_BOOST_PYTHON)
@@ -134,9 +133,16 @@ set(LEDGER_INCLUDES
value.h
views.h
xact.h
- strptime.h
${PROJECT_BINARY_DIR}/system.hh)
+# Windows provides no strptime(), so supply our own.
+if (WIN32 OR CYGWIN)
+ list(APPEND LEDGER_INCLUDES
+ strptime.h)
+ list(APPEND LEDGER_SOURCES
+ strptime.cc)
+endif()
+
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
if (CMAKE_CXX_COMPILER MATCHES "clang\\+\\+")
add_definitions(
diff --git a/src/amount.cc b/src/amount.cc
index 05145f87..c6463b2b 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -604,7 +604,9 @@ void amount_t::in_place_invert()
throw_(amount_error, _("Cannot invert an uninitialized amount"));
_dup();
- mpq_inv(MP(quantity), MP(quantity));
+
+ if (sign() != 0)
+ mpq_inv(MP(quantity), MP(quantity));
}
void amount_t::in_place_round()
diff --git a/src/annotate.cc b/src/annotate.cc
index ab81d412..c5ccdf07 100644
--- a/src/annotate.cc
+++ b/src/annotate.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -81,7 +81,7 @@ bool annotation_t::operator<(const annotation_t& rhs) const
void annotation_t::parse(std::istream& in)
{
do {
- istream_pos_type pos = in.tellg();
+ std::istream::pos_type pos = in.tellg();
if (static_cast<int>(pos) < 0)
return;
diff --git a/src/commodity.cc b/src/commodity.cc
index a8520ca1..d6d5ca98 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -294,7 +294,7 @@ bool commodity_t::symbol_needs_quotes(const string& symbol)
void commodity_t::parse_symbol(std::istream& in, string& symbol)
{
- istream_pos_type pos = in.tellg();
+ std::istream::pos_type pos = in.tellg();
char buf[256];
char c = peek_next_nonws(in);
diff --git a/src/context.h b/src/context.h
index 4a7a5441..ca7af060 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -58,18 +58,19 @@ public:
shared_ptr<std::istream> stream;
- path pathname;
- path current_directory;
- journal_t * journal;
- account_t * master;
- scope_t * scope;
- char linebuf[MAX_LINE + 1];
- istream_pos_type line_beg_pos;
- istream_pos_type curr_pos;
- std::size_t linenum;
- std::size_t errors;
- std::size_t count;
- std::size_t sequence;
+ path pathname;
+ path current_directory;
+ journal_t * journal;
+ account_t * master;
+ scope_t * scope;
+ char linebuf[MAX_LINE + 1];
+ std::istream::pos_type line_beg_pos;
+ std::istream::pos_type curr_pos;
+ std::size_t linenum;
+ std::size_t errors;
+ std::size_t count;
+ std::size_t sequence;
+ std::string last;
explicit parse_context_t(const path& cwd)
: current_directory(cwd), master(NULL), scope(NULL),
@@ -112,11 +113,7 @@ inline parse_context_t open_for_reading(const path& pathname,
const path& cwd)
{
path filename = resolve_path(pathname);
-#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3
filename = filesystem::absolute(filename, cwd);
-#else
- filename = filesystem::complete(filename, cwd);
-#endif
if (! exists(filename) || is_directory(filename))
throw_(std::runtime_error,
_f("Cannot read journal file %1%") % filename);
diff --git a/src/draft.cc b/src/draft.cc
index 424b7a9a..12d7faf7 100644
--- a/src/draft.cc
+++ b/src/draft.cc
@@ -68,7 +68,7 @@ void draft_t::xact_template_t::dump(std::ostream& out) const
} else {
foreach (const post_template_t& post, posts) {
out << std::endl
- << _f("[Posting \"%1\"]") % (post.from ? _("from") : _("to"))
+ << _f("[Posting \"%1%\"]") % (post.from ? _("from") : _("to"))
<< std::endl;
if (post.account_mask)
diff --git a/src/error.cc b/src/error.cc
index 7e49645b..1ab92840 100644
--- a/src/error.cc
+++ b/src/error.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -78,10 +78,10 @@ string line_context(const string& line,
return buf.str();
}
-string source_context(const path& file,
- const istream_pos_type pos,
- const istream_pos_type end_pos,
- const string& prefix)
+string source_context(const path& file,
+ const std::istream::pos_type pos,
+ const std::istream::pos_type end_pos,
+ const string& prefix)
{
const std::streamoff len = end_pos - pos;
if (! len || file.empty())
diff --git a/src/error.h b/src/error.h
index 88e09329..3bdcde98 100644
--- a/src/error.h
+++ b/src/error.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -47,7 +47,7 @@ namespace ledger {
extern std::ostringstream _desc_buffer;
template <typename T>
-inline void throw_func(const string& message) {
+[[ noreturn ]] inline void throw_func(const string& message) {
_desc_buffer.clear();
_desc_buffer.str("");
throw T(message);
@@ -81,10 +81,10 @@ string line_context(const string& line,
const string::size_type pos = 0,
const string::size_type end_pos = 0);
-string source_context(const path& file,
- const istream_pos_type pos,
- const istream_pos_type end_pos,
- const string& prefix = "");
+string source_context(const path& file,
+ const std::istream::pos_type pos,
+ const std::istream::pos_type end_pos,
+ const string& prefix = "");
#define DECLARE_EXCEPTION(name, kind) \
class name : public kind { \
@@ -95,8 +95,9 @@ string source_context(const path& file,
struct error_count {
std::size_t count;
- explicit error_count(std::size_t _count) : count(_count) {}
- const char * what() const { return ""; }
+ std::string message;
+ explicit error_count(std::size_t _count, std::string _msg) : count(_count), message(_msg) {}
+ const char * what() const { return message.c_str(); }
};
} // namespace ledger
diff --git a/src/expr.cc b/src/expr.cc
index 85818e4b..c8945d3d 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -94,9 +94,9 @@ void expr_t::parse(std::istream& in, const parse_flags_t& flags,
const optional<string>& original_string)
{
parser_t parser;
- istream_pos_type start_pos = in.tellg();
+ std::istream::pos_type start_pos = in.tellg();
ptr = parser.parse(in, flags, original_string);
- istream_pos_type end_pos = in.tellg();
+ std::istream::pos_type end_pos = in.tellg();
if (original_string) {
set_text(*original_string);
@@ -290,10 +290,10 @@ value_t source_command(call_scope_t& args)
in = &std::cin;
}
- symbol_scope_t file_locals(args);
- std::size_t linenum = 0;
- char buf[4096];
- istream_pos_type pos;
+ symbol_scope_t file_locals(args);
+ std::size_t linenum = 0;
+ char buf[4096];
+ std::istream::pos_type pos;
while (in->good() && ! in->eof()) {
pos = in->tellg();
diff --git a/src/format.cc b/src/format.cc
index bb578141..5b9baa21 100644
--- a/src/format.cc
+++ b/src/format.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -103,7 +103,7 @@ namespace {
p += std::strlen(p);
} else {
assert(str.good());
- istream_pos_type pos = str.tellg();
+ std::istream::pos_type pos = str.tellg();
expr.set_text(string(p, p + long(pos)));
p += long(pos) - 1;
diff --git a/src/global.h b/src/global.h
index 01ad60cc..8f8266ac 100644
--- a/src/global.h
+++ b/src/global.h
@@ -166,7 +166,7 @@ See LICENSE file included with the distribution for details and disclaimer.");
OPTION_(global_scope_t, version, DO() { // -v
parent->show_version_info(std::cout);
- throw error_count(0); // exit immediately
+ throw error_count(0, ""); // exit immediately
});
};
diff --git a/src/item.cc b/src/item.cc
index bd025c52..7132103e 100644
--- a/src/item.cc
+++ b/src/item.cc
@@ -152,7 +152,7 @@ void item_t::parse_tags(const char * p,
if (const char * b = std::strchr(p, '[')) {
if (*(b + 1) != '\0' &&
(std::isdigit(*(b + 1)) || *(b + 1) == '=')) {
- if (const char * e = std::strchr(p, ']')) {
+ if (const char * e = std::strchr(b, ']')) {
char buf[256];
std::strncpy(buf, b + 1, static_cast<std::size_t>(e - b - 1));
buf[e - b - 1] = '\0';
diff --git a/src/item.h b/src/item.h
index a28bf59e..51539eff 100644
--- a/src/item.h
+++ b/src/item.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -48,12 +48,12 @@ namespace ledger {
struct position_t
{
- path pathname;
- istream_pos_type beg_pos;
- std::size_t beg_line;
- istream_pos_type end_pos;
- std::size_t end_line;
- std::size_t sequence;
+ path pathname;
+ std::istream::pos_type beg_pos;
+ std::size_t beg_line;
+ std::istream::pos_type end_pos;
+ std::size_t end_line;
+ std::size_t sequence;
position_t()
: beg_pos(0), beg_line(0), end_pos(0), end_line(0), sequence(0) {
diff --git a/src/main.cc b/src/main.cc
index c8b9ec3a..218579cf 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -35,6 +35,10 @@
// was moved there for the sake of clarity here
#include "session.h"
+#ifdef HAVE_EDIT
+#include <editline/readline.h>
+#endif
+
using namespace ledger;
#if HAVE_BOOST_PYTHON
@@ -69,9 +73,6 @@ int main(int argc, char * argv[], char * envp[])
// Initialize global Boost/C++ environment
std::ios::sync_with_stdio(false);
-#if BOOST_VERSION < 104600
- filesystem::path::default_name_check(filesystem::portable_posix_name);
-#endif
std::signal(SIGINT, sigint_handler);
#if !defined(_WIN32) && !defined(__CYGWIN__)
@@ -128,13 +129,9 @@ int main(int argc, char * argv[], char * envp[])
bool exit_loop = false;
-#if HAVE_EDIT
-
+#ifdef HAVE_EDIT
rl_readline_name = const_cast<char *>("Ledger");
-#if 0
- // jww (2009-02-05): NYI
- rl_attempted_completion_function = ledger_completion;
-#endif
+ // TODO: rl_attempted_completion_function = ledger_completion;
while (char * p = readline(global_scope->prompt_string())) {
char * expansion = NULL;
@@ -173,7 +170,7 @@ int main(int argc, char * argv[], char * envp[])
global_scope->execute_command_wrapper(split_arguments(p), true);
}
-#if HAVE_EDIT
+#ifdef HAVE_EDIT
if (expansion)
std::free(expansion);
std::free(p);
diff --git a/src/mask.cc b/src/mask.cc
index 434acad6..35e690de 100644
--- a/src/mask.cc
+++ b/src/mask.cc
@@ -75,9 +75,8 @@ mask_t& mask_t::assign_glob(const string& pat)
if (i + 1 < len) {
re_pat += pat[++i];
break;
- } else {
- // fallthrough...
}
+ // fallthrough...
default:
re_pat += pat[i];
break;
diff --git a/src/op.cc b/src/op.cc
index 00b84459..5801b1dd 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -963,7 +963,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
string op_context(const expr_t::ptr_op_t op,
const expr_t::ptr_op_t locus)
{
- ostream_pos_type start_pos, end_pos;
+ std::ostream::pos_type start_pos, end_pos;
expr_t::op_t::context_t context(op, locus, &start_pos, &end_pos);
std::ostringstream buf;
buf << " ";
diff --git a/src/op.h b/src/op.h
index bbbb5d81..aab362c7 100644
--- a/src/op.h
+++ b/src/op.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -285,19 +285,19 @@ public:
struct context_t
{
- ptr_op_t expr_op;
- ptr_op_t op_to_find;
- ostream_pos_type * start_pos;
- ostream_pos_type * end_pos;
- bool relaxed;
+ ptr_op_t expr_op;
+ ptr_op_t op_to_find;
+ std::ostream::pos_type * start_pos;
+ std::ostream::pos_type * end_pos;
+ bool relaxed;
context_t() : start_pos(NULL), end_pos(NULL), relaxed(false) {}
- context_t(const ptr_op_t& _expr_op,
- const ptr_op_t& _op_to_find,
- ostream_pos_type * const _start_pos = NULL,
- ostream_pos_type * const _end_pos = NULL,
- const bool _relaxed = true)
+ context_t(const ptr_op_t& _expr_op,
+ const ptr_op_t& _op_to_find,
+ std::ostream::pos_type * const _start_pos = NULL,
+ std::ostream::pos_type * const _end_pos = NULL,
+ const bool _relaxed = true)
: expr_op(_expr_op), op_to_find(_op_to_find),
start_pos(_start_pos), end_pos(_end_pos),
relaxed(_relaxed) {}
diff --git a/src/output.cc b/src/output.cc
index c2fa83ac..09d3ad9e 100644
--- a/src/output.cc
+++ b/src/output.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -284,8 +284,9 @@ void report_accounts::flush()
std::ostream& out(report.output_stream);
format_t prepend_format;
std::size_t prepend_width;
+ bool do_prepend_format;
- if (report.HANDLED(prepend_format_)) {
+ if ((do_prepend_format = report.HANDLED(prepend_format_))) {
prepend_format.parse_format(report.HANDLER(prepend_format_).str());
prepend_width = report.HANDLED(prepend_width_)
? lexical_cast<std::size_t>(report.HANDLER(prepend_width_).str())
@@ -293,7 +294,7 @@ void report_accounts::flush()
}
foreach (accounts_pair& entry, accounts) {
- if (prepend_format) {
+ if (do_prepend_format) {
bind_scope_t bound_scope(report, *entry.first);
out.width(static_cast<std::streamsize>(prepend_width));
out << prepend_format(bound_scope);
diff --git a/src/py_post.cc b/src/py_post.cc
index 6195570b..525b47c2 100644
--- a/src/py_post.cc
+++ b/src/py_post.cc
@@ -153,6 +153,11 @@ void export_post()
return_value_policy<return_by_value>()),
make_setter(&post_t::cost,
return_value_policy<return_by_value>()))
+ .add_property("given_cost",
+ make_getter(&post_t::given_cost,
+ return_value_policy<return_by_value>()),
+ make_setter(&post_t::given_cost,
+ return_value_policy<return_by_value>()))
.add_property("assigned_amount",
make_getter(&post_t::assigned_amount,
return_value_policy<return_by_value>()),
diff --git a/src/py_value.cc b/src/py_value.cc
index f4f63946..486228c0 100644
--- a/src/py_value.cc
+++ b/src/py_value.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -88,12 +88,6 @@ namespace {
return buf.str();
}
- string py_dump_relaxed(const value_t& value) {
- std::ostringstream buf;
- value.dump(buf, true);
- return buf.str();
- }
-
void py_set_string(value_t& value, const string& str) {
return value.set_string(str);
}
diff --git a/src/pyinterp.cc b/src/pyinterp.cc
index 19430bef..fad0b559 100644
--- a/src/pyinterp.cc
+++ b/src/pyinterp.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -222,27 +222,13 @@ object python_interpreter_t::import_option(const string& str)
python::list paths(sys_dict["path"]);
if (contains(str, ".py")) {
-#if BOOST_VERSION >= 103700
path& cwd(parsing_context.get_current().current_directory);
-#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3
path parent(filesystem::absolute(file, cwd).parent_path());
-#else
- path parent(filesystem::complete(file, cwd).parent_path());
-#endif
DEBUG("python.interp", "Adding " << parent << " to PYTHONPATH");
paths.insert(0, parent.string());
sys_dict["path"] = paths;
-#if BOOST_VERSION >= 104600
name = file.stem().string();
-#else
- name = file.stem();
-#endif
-#else // BOOST_VERSION >= 103700
- paths.insert(0, file.branch_path().string());
- sys_dict["path"] = paths;
- name = file.leaf();
-#endif // BOOST_VERSION >= 103700
}
try {
diff --git a/src/query.cc b/src/query.cc
index fc1d4ff0..883bea40 100644
--- a/src/query.cc
+++ b/src/query.cc
@@ -155,6 +155,7 @@ query_t::lexer_t::next_token(query_t::lexer_t::token_t::kind_t tok_context)
case ')':
if (! consume_next && tok_context == token_t::TOK_EXPR)
goto test_ident;
+ // fall through...
case '(':
case '&':
case '|':
diff --git a/src/quotes.cc b/src/quotes.cc
index 50791560..8ae8ca90 100644
--- a/src/quotes.cc
+++ b/src/quotes.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -79,13 +79,8 @@ commodity_quote_from_script(commodity_t& commodity,
if (optional<std::pair<commodity_t *, price_point_t> > point =
commodity_pool_t::current_pool->parse_price_directive(buf)) {
if (commodity_pool_t::current_pool->price_db) {
-#if defined(__GNUG__) && __GNUG__ < 3
- ofstream database(*commodity_pool_t::current_pool->price_db,
- ios::out | ios::app);
-#else
ofstream database(*commodity_pool_t::current_pool->price_db,
std::ios_base::out | std::ios_base::app);
-#endif
database << "P "
<< format_datetime(point->second.when, FMT_WRITTEN)
<< " " << commodity.symbol()
diff --git a/src/session.cc b/src/session.cc
index e95123e8..427850d9 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -76,7 +76,7 @@ std::size_t session_t::read_data(const string& master_account)
file = path(home_var) / ".ledger";
if (! file.empty() && exists(file))
- HANDLER(file_).data_files.insert(file);
+ HANDLER(file_).data_files.push_back(file);
else
throw_(parse_error, "No journal file was specified (please use -f)");
@@ -214,7 +214,7 @@ journal_t * session_t::read_journal_files()
journal_t * session_t::read_journal(const path& pathname)
{
HANDLER(file_).data_files.clear();
- HANDLER(file_).data_files.insert(pathname);
+ HANDLER(file_).data_files.push_back(pathname);
return read_journal_files();
}
diff --git a/src/session.h b/src/session.h
index 47732a3d..4dce3816 100644
--- a/src/session.h
+++ b/src/session.h
@@ -153,14 +153,14 @@ public:
OPTION__
(session_t, file_, // -f
- std::set<path COMMA ComparePaths> data_files;
+ std::list<path> data_files;
CTOR(session_t, file_) {}
DO_(str) {
if (parent->flush_on_next_data_file) {
data_files.clear();
parent->flush_on_next_data_file = false;
}
- data_files.insert(str);
+ data_files.push_back(str);
});
OPTION_(session_t, input_date_format_, DO_(str) {
diff --git a/src/stream.cc b/src/stream.cc
index c4bbb42e..91a190d8 100644
--- a/src/stream.cc
+++ b/src/stream.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -89,11 +89,7 @@ namespace {
else { // parent
close(pfd[0]);
typedef iostreams::stream<iostreams::file_descriptor_sink> fdstream;
-#if BOOST_VERSION >= 104400
*os = new fdstream(pfd[1], iostreams::never_close_handle);
-#else // BOOST_VERSION >= 104400
- *os = new fdstream(pfd[1]);
-#endif // BOOST_VERSION >= 104400
}
return pfd[1];
#else
diff --git a/src/strptime.cc b/src/strptime.cc
index 069b9267..ac6885a6 100644
--- a/src/strptime.cc
+++ b/src/strptime.cc
@@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#if defined(_WIN32) || defined(__CYGWIN__)
+#if !(defined(_WIN32) || defined(__CYGWIN__))
+#error This file should only be compiled on Windows.
+#endif
+
// Implement strptime under windows
#include "strptime.h"
@@ -200,5 +203,3 @@ static char* _strptime(const char *s, const char *format, struct tm *tm) {
char* strptime(const char *buf, const char *fmt, struct tm *tm) {
return _strptime(buf, fmt, tm);
}
-
-#endif // _WIN32
diff --git a/src/system.hh.in b/src/system.hh.in
index 38ac1e63..97651e8b 100644
--- a/src/system.hh.in
+++ b/src/system.hh.in
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -57,11 +57,9 @@
#define Ledger_VERSION_PRERELEASE "@Ledger_VERSION_PRERELEASE@"
#define Ledger_VERSION_DATE @Ledger_VERSION_DATE@
-#define HAVE_EDIT @HAVE_EDIT@
#define HAVE_GETTEXT @HAVE_GETTEXT@
-#cmakedefine HAVE_ACCESS
-#cmakedefine HAVE_REALPATH
+#cmakedefine HAVE_EDIT
#cmakedefine HAVE_GETPWUID
#cmakedefine HAVE_GETPWNAM
#cmakedefine HAVE_IOCTL
@@ -83,14 +81,9 @@
/* System includes */
/*------------------------------------------------------------------------*/
-#if defined(__GNUG__) && __GNUG__ < 3
-#define _XOPEN_SOURCE
-#endif
-
#include <algorithm>
#include <exception>
#include <typeinfo>
-#include <locale>
#include <stdexcept>
#include <iostream>
#include <streambuf>
@@ -107,29 +100,6 @@
#include <string>
#include <vector>
-#if defined(__GNUG__) && __GNUG__ < 3
-
-namespace std {
- inline ostream & right (ostream & i) {
- i.setf(i.right, i.adjustfield);
- return i;
- }
- inline ostream & left (ostream & i) {
- i.setf(i.left, i.adjustfield);
- return i;
- }
-}
-
-typedef std::streamoff istream_pos_type;
-typedef std::streamoff ostream_pos_type;
-
-#else // ! (defined(__GNUG__) && __GNUG__ < 3)
-
-typedef std::istream::pos_type istream_pos_type;
-typedef std::ostream::pos_type ostream_pos_type;
-
-#endif
-
#include <cassert>
#include <cctype>
#include <cstdarg>
@@ -138,12 +108,6 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <cstring>
#include <csignal>
-#if defined __FreeBSD__ && __FreeBSD__ <= 4
-// FreeBSD has a broken isspace macro, so don't use it
-#undef isspace(c)
-#endif
-
-#include <sys/stat.h>
#if defined(_WIN32) || defined(__CYGWIN__)
#include <io.h>
#else
@@ -167,10 +131,6 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <mpfr.h>
#include "utf8.h"
-#if HAVE_EDIT
-#include <editline/readline.h>
-#endif
-
#include <boost/algorithm/string.hpp>
#include <boost/any.hpp>
#include <boost/bind.hpp>
@@ -247,7 +207,6 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <boost/python/detail/wrap_python.hpp>
#include <datetime.h>
-#include <unicodeobject.h>
#include <boost/python/module_init.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
diff --git a/src/textual.cc b/src/textual.cc
index 8fbc5c08..04e90e45 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -282,6 +282,10 @@ void instance_t::parse()
std::cerr << _("Error: ") << err.what() << std::endl;
context.errors++;
+ if (! current_context.empty())
+ context.last = current_context + "\n" + err.what();
+ else
+ context.last = err.what();
}
}
@@ -553,7 +557,7 @@ void instance_t::option_directive(char * line)
void instance_t::automated_xact_directive(char * line)
{
- istream_pos_type pos = context.line_beg_pos;
+ std::istream::pos_type pos = context.line_beg_pos;
bool reveal_context = true;
@@ -649,7 +653,7 @@ void instance_t::automated_xact_directive(char * line)
void instance_t::period_xact_directive(char * line)
{
- istream_pos_type pos = context.line_beg_pos;
+ std::istream::pos_type pos = context.line_beg_pos;
bool reveal_context = true;
@@ -739,17 +743,8 @@ void instance_t::include_directive(char * line)
DEBUG("textual.include", "resolved path: " << filename.string());
mask_t glob;
-#if BOOST_VERSION >= 103700
path parent_path = filename.parent_path();
-#if BOOST_VERSION >= 104600
glob.assign_glob('^' + filename.filename().string() + '$');
-#else
- glob.assign_glob('^' + filename.filename() + '$');
-#endif
-#else // BOOST_VERSION >= 103700
- path parent_path = filename.branch_path();
- glob.assign_glob('^' + filename.leaf() + '$');
-#endif // BOOST_VERSION >= 103700
bool files_found = false;
if (exists(parent_path)) {
@@ -757,21 +752,9 @@ void instance_t::include_directive(char * line)
for (filesystem::directory_iterator iter(parent_path);
iter != end;
++iter) {
-#if BOOST_VERSION <= 103500
- if (is_regular(*iter))
-#else
if (is_regular_file(*iter))
-#endif
{
-#if BOOST_VERSION >= 103700
-#if BOOST_VERSION >= 104600
string base = (*iter).path().filename().string();
-#else
- string base = (*iter).filename();
-#endif
-#else // BOOST_VERSION >= 103700
- string base = (*iter).leaf();
-#endif // BOOST_VERSION >= 103700
if (glob.match(base)) {
journal_t * journal = context.journal;
account_t * master = top_account();
@@ -912,8 +895,8 @@ void instance_t::end_apply_directive(char * kind)
void instance_t::account_directive(char * line)
{
- istream_pos_type beg_pos = context.line_beg_pos;
- std::size_t beg_linenum = context.linenum;
+ std::istream::pos_type beg_pos = context.line_beg_pos;
+ std::size_t beg_linenum = context.linenum;
char * p = skip_ws(line);
account_t * account =
@@ -1644,29 +1627,30 @@ post_t * instance_t::parse_post(char * line,
}
DEBUG("textual.parse", "line " << context.linenum << ": "
- << "POST assign: parsed amt = " << *post->assigned_amount);
+ << "POST assign: parsed balance amount = " << *post->assigned_amount);
- amount_t& amt(*post->assigned_amount);
+ const amount_t& amt(*post->assigned_amount);
value_t account_total
(post->account->amount().strip_annotations(keep_details_t()));
DEBUG("post.assign", "line " << context.linenum << ": "
<< "account balance = " << account_total);
- DEBUG("post.assign",
- "line " << context.linenum << ": " << "post amount = " << amt);
+ DEBUG("post.assign", "line " << context.linenum << ": "
+ << "post amount = " << amt << " (is_zero = " << amt.is_zero() << ")");
- amount_t diff = amt;
+ balance_t diff = amt;
switch (account_total.type()) {
case value_t::AMOUNT:
- if (account_total.as_amount().commodity_ptr() == diff.commodity_ptr())
- diff -= account_total.as_amount();
+ diff -= account_total.as_amount();
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Subtracting amount " << account_total.as_amount() << " from diff, yielding " << diff);
break;
case value_t::BALANCE:
- if (optional<amount_t> comm_bal =
- account_total.as_balance().commodity_amount(amt.commodity()))
- diff -= *comm_bal;
+ diff -= account_total.as_balance();
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Subtracting balance " << account_total.as_balance() << " from diff, yielding " << diff);
break;
default:
@@ -1680,18 +1664,34 @@ post_t * instance_t::parse_post(char * line,
// Subtract amounts from previous posts to this account in the xact.
for (post_t* p : xact->posts) {
- if (p->account == post->account &&
- p->amount.commodity_ptr() == diff.commodity_ptr()) {
+ if (p->account == post->account) {
diff -= p->amount;
DEBUG("textual.parse", "line " << context.linenum << ": "
- << "Subtract " << p->amount << ", diff = " << diff);
+ << "Subtracting " << p->amount << ", diff = " << diff);
}
}
+ // If amt has a commodity, restrict balancing to that. Otherwise, it's the blanket '0' and
+ // check that all of them are zero.
+ if (amt.has_commodity()) {
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Finding commodity " << amt.commodity() << " (" << amt << ") in balance " << diff);
+ optional<amount_t> wanted_commodity = diff.commodity_amount(amt.commodity());
+ if (!wanted_commodity) {
+ diff = amt - amt; // this is '0' with the correct commodity.
+ } else {
+ diff = *wanted_commodity;
+ }
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Diff is now " << diff);
+ }
+
if (post->amount.is_null()) {
// balance assignment
if (! diff.is_zero()) {
- post->amount = diff;
+ // This will fail if there are more than 1 commodity in diff, which is wanted,
+ // as amount cannot store more than 1 commodity.
+ post->amount = diff.to_amount();
DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Overwrite null posting");
}
@@ -1699,10 +1699,11 @@ post_t * instance_t::parse_post(char * line,
// balance assertion
diff -= post->amount;
if (! no_assertions && ! diff.is_zero()) {
- amount_t tot = amt - diff;
+ balance_t tot = -diff + amt;
+ DEBUG("textual.parse", "Balance assertion: off by " << diff << " (expected to see " << tot << ")");
throw_(parse_error,
_f("Balance assertion off by %1% (expected to see %2%)")
- % diff % tot);
+ % diff.to_string() % tot.to_string());
}
}
@@ -2012,7 +2013,8 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack)
TRACE_FINISH(parsing_total, 1);
if (context_stack.get_current().errors > 0)
- throw error_count(context_stack.get_current().errors);
+ throw error_count(context_stack.get_current().errors,
+ context_stack.get_current().last);
return context_stack.get_current().count;
}
diff --git a/src/times.cc b/src/times.cc
index eda71ae7..db0d74ff 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -43,23 +43,11 @@ optional<datetime_t> epoch;
date_time::weekdays start_of_week = gregorian::Sunday;
-//#define USE_BOOST_FACETS 1
-#if defined(USE_BOOST_FACETS)
-#error "Boost facets are not quite working yet"
-#endif
-
namespace {
template <typename T, typename InputFacetType, typename OutputFacetType>
class temporal_io_t : public noncopyable
{
- string fmt_str;
-#if defined(USE_BOOST_FACETS)
- std::istringstream input_stream;
- std::ostringstream output_stream;
- InputFacetType * input_facet;
- OutputFacetType * output_facet;
- std::string temp_string;
-#endif // USE_BOOST_FACETS
+ string fmt_str;
public:
date_traits_t traits;
@@ -71,15 +59,6 @@ namespace {
icontains(fmt_str, "%m") || icontains(fmt_str, "%b"),
icontains(fmt_str, "%d")),
input(_input) {
-#if defined(USE_BOOST_FACETS)
- if (input) {
- input_facet = new InputFacetType(fmt_str);
- input_stream.imbue(std::locale(std::locale::classic(), input_facet));
- } else {
- output_facet = new OutputFacetType(fmt_str);
- output_stream.imbue(std::locale(std::locale::classic(), output_facet));
- }
-#endif // USE_BOOST_FACETS
}
void set_format(const char * fmt) {
@@ -88,29 +67,15 @@ namespace {
icontains(fmt_str, "%m") ||
icontains(fmt_str, "%b"),
icontains(fmt_str, "%d"));
-#if defined(USE_BOOST_FACETS)
- if (input)
- input_facet->format(fmt_str);
- else
- output_facet->format(fmt_str);
-#endif // USE_BOOST_FACETS
}
T parse(const char *) {}
std::string format(const T& when) {
-#if defined(USE_BOOST_FACETS)
- output_stream.str(temp_string);
- output_stream.seekp(std::ios_base::beg);
- output_stream.clear();
- output_stream << when;
- return output_stream.str();
-#else // USE_BOOST_FACETS
std::tm data(to_tm(when));
char buf[128];
std::strftime(buf, 127, fmt_str.c_str(), &data);
return buf;
-#endif // USE_BOOST_FACETS
}
};
@@ -119,34 +84,12 @@ namespace {
posix_time::time_facet>
::parse(const char * str)
{
-#if defined(USE_BOOST_FACETS)
- input_stream.seekg(std::ios_base::beg);
- input_stream.clear();
- input_stream.str(str);
-
- datetime_t when;
- input_stream >> when;
-#if DEBUG_ON
- if (when.is_not_a_date_time())
- DEBUG("times.parse", "Failed to parse date/time '" << str
- << "' using pattern '" << fmt_str << "'");
-#endif
-
- if (! when.is_not_a_date_time() &&
- input_stream.good() && ! input_stream.eof() &&
- input_stream.peek() != EOF) {
- DEBUG("times.parse", "This string has leftovers: '" << str << "'");
- return datetime_t();
- }
- return when;
-#else // USE_BOOST_FACETS
std::tm data;
std::memset(&data, 0, sizeof(std::tm));
if (strptime(str, fmt_str.c_str(), &data))
return posix_time::ptime_from_tm(data);
else
return datetime_t();
-#endif // USE_BOOST_FACETS
}
template <>
@@ -154,27 +97,6 @@ namespace {
gregorian::date_facet>
::parse(const char * str)
{
-#if defined(USE_BOOST_FACETS)
- input_stream.seekg(std::ios_base::beg);
- input_stream.clear();
- input_stream.str(str);
-
- date_t when;
- input_stream >> when;
-#if DEBUG_ON
- if (when.is_not_a_date())
- DEBUG("times.parse", "Failed to parse date '" << str
- << "' using pattern '" << fmt_str << "'");
-#endif
-
- if (! when.is_not_a_date() &&
- input_stream.good() && ! input_stream.eof() &&
- input_stream.peek() != EOF) {
- DEBUG("times.parse", "This string has leftovers: '" << str << "'");
- return date_t();
- }
- return when;
-#else // USE_BOOST_FACETS
std::tm data;
std::memset(&data, 0, sizeof(std::tm));
data.tm_year = CURRENT_DATE().year() - 1900;
@@ -183,7 +105,6 @@ namespace {
return gregorian::date_from_tm(data);
else
return date_t();
-#endif // USE_BOOST_FACETS
}
typedef temporal_io_t<datetime_t, posix_time::time_input_facet,
diff --git a/src/token.cc b/src/token.cc
index 1ec052ed..76bf5106 100644
--- a/src/token.cc
+++ b/src/token.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -380,7 +380,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
default: {
- istream_pos_type pos = in.tellg();
+ std::istream::pos_type pos = in.tellg();
// First, check to see if it's a reserved word, such as: and or not
int result = parse_reserved_word(in);
diff --git a/src/utils.h b/src/utils.h
index b21dff7a..c9146dd7 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -44,7 +44,11 @@
#ifndef _UTILS_H
#define _UTILS_H
+#if (BOOST_VERSION >= 106600)
+#include <boost/uuid/detail/sha1.hpp>
+#else
#include <boost/uuid/sha1.hpp>
+#endif
/**
* @name Default values
@@ -496,10 +500,6 @@ inline T& downcast(U& object) {
path resolve_path(const path& pathname);
-#ifdef HAVE_REALPATH
-extern "C" char * realpath(const char *, char resolved_path[]);
-#endif
-
inline const string& either_or(const string& first,
const string& second) {
return first.empty() ? second : first;
diff --git a/src/value.cc b/src/value.cc
index 1841505d..5ce30530 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -129,11 +129,7 @@ value_t::operator bool() const
void value_t::set_type(type_t new_type)
{
if (new_type == VOID) {
-#if BOOST_VERSION >= 103700
storage.reset();
-#else
- storage = intrusive_ptr<storage_t>();
-#endif
} else {
if (! storage || storage->refc > 1)
storage = new storage_t;
diff --git a/src/value.h b/src/value.h
index 35581bfb..e44f9354 100644
--- a/src/value.h
+++ b/src/value.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -878,22 +878,14 @@ public:
VERIFY(! is_null());
if (! is_sequence()) {
-#if BOOST_VERSION >= 103700
storage.reset();
-#else
- storage = intrusive_ptr<storage_t>();
-#endif
} else {
as_sequence_lval().pop_back();
const sequence_t& seq(as_sequence());
std::size_t new_size = seq.size();
if (new_size == 0) {
-#if BOOST_VERSION >= 103700
storage.reset();
-#else
- storage = intrusive_ptr<storage_t>();
-#endif
}
else if (new_size == 1) {
*this = seq.front();
diff --git a/src/xact.cc b/src/xact.cc
index 5df9ebc5..10a7106a 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -395,10 +395,12 @@ bool xact_base_t::finalize()
some_null = true;
}
- if (post->has_flags(POST_DEFERRED))
- post->account->add_deferred_post(id(), post);
- else
+ if (post->has_flags(POST_DEFERRED)) {
+ if (!post->amount.is_null())
+ 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);
diff --git a/src/xact.h b/src/xact.h
index b0a4417e..26e263fc 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -115,7 +115,7 @@ public:
virtual string description() {
if (pos) {
std::ostringstream buf;
- buf << _f("transaction at line %1") << pos->beg_line;
+ buf << _f("transaction at line %1%") % pos->beg_line;
return buf.str();
} else {
return string(_("generated transaction"));
@@ -177,7 +177,7 @@ public:
virtual string description() {
if (pos) {
std::ostringstream buf;
- buf << _f("automated transaction at line %1") << pos->beg_line;
+ buf << _f("automated transaction at line %1%") % pos->beg_line;
return buf.str();
} else {
return string(_("generated automated transaction"));
@@ -220,7 +220,7 @@ class period_xact_t : public xact_base_t
virtual string description() {
if (pos) {
std::ostringstream buf;
- buf << _f("periodic transaction at line %1") << pos->beg_line;
+ buf << _f("periodic transaction at line %1%") % pos->beg_line;
return buf.str();
} else {
return string(_("generated periodic transaction"));
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index d0b62f6e..41eecb36 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -27,8 +27,6 @@ macro(add_ledger_harness_tests _class)
${TestFile} ${TEST_PYTHON_FLAGS})
set_tests_properties(${_class}Test_${TestFile_Name}
PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE}")
- set_target_properties(check
- PROPERTIES DEPENDS ${_class}Test_${TestFile_Name})
endif()
endforeach()
endif()
@@ -48,8 +46,6 @@ if (PYTHONINTERP_FOUND)
--ledger $<TARGET_FILE:ledger> --file ${TestFile})
set_tests_properties(${_class}Test_${TestFile_Name}
PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE}")
- set_target_properties(check
- PROPERTIES DEPENDS ${_class}Test_${TestFile_Name})
endforeach()
# CheckManpage and CheckTexinfo are disabled, since they do not work
diff --git a/test/python/JournalTest.py b/test/python/JournalTest.py
index e65c671d..2565ede8 100644
--- a/test/python/JournalTest.py
+++ b/test/python/JournalTest.py
@@ -22,6 +22,24 @@ class JournalTestCase(unittest.TestCase):
for post in journal.query("food"):
self.assertEqual(str(post.account), "Expenses:Food")
self.assertEqual(post.amount, Amount("$21.34"))
+
+ def testParseError(self):
+ # TODO: ledger spits out parse errors to standard out.
+ # This should not happen, especially when the error
+ # has already been captured by a Python exception.
+ def fun():
+ read_journal_from_string("""
+2012-03-01 KFC
+ Expenses:Food rsnetnirsnti
+ Assets:Cash
+""")
+ self.assertRaises(RuntimeError, fun)
+ try:
+ fun()
+ except RuntimeError as e:
+ self.assertEquals(str(e).splitlines()[-1],
+ "No quantity specified for amount")
+
def suite():
return unittest.TestLoader().loadTestsFromTestCase(JournalTestCase)
diff --git a/test/regress/1187_5.test b/test/regress/1187_5.test
new file mode 100644
index 00000000..4aa5fdd8
--- /dev/null
+++ b/test/regress/1187_5.test
@@ -0,0 +1,36 @@
+2013/12/01 * Initial State
+ Crédit:Viseca:MasterCard P1 -618.50 CHF
+ Crédit:Viseca:MasterCard P2 -52.10 CHF
+ Equity:Opening Balances
+
+2013/12/15 * Buy Some Chocolate
+ Dépenses:Nourriture 19.00 EUR ; #1
+ Crédit:Viseca:MasterCard P1
+
+2013/12/15 * Buy Some Chocolate
+ Crédit:Viseca:MasterCard P1 18.00 EUR ; #2
+ Recettes:Erreurs
+
+2013/12/23 * Facture Viseca
+ Crédit:Viseca:MasterCard P2 52.10 CHF = 0 ; #3
+ Crédit:Viseca:MasterCard P1 618.50 CHF = 0 CHF ; #4
+ Dépenses:Frais:Gestion Comptes 1.50 CHF
+ Crédit:Viseca -672.10 CHF
+
+2014/01/03 * Facture Viseca
+ Crédit:Viseca 672.10 CHF = 0
+ Actif:Comptes:CP courant
+
+test bal
+ -672.10 CHF Actif:Comptes:CP courant
+ -1.00 EUR Crédit:Viseca
+ -1.00 EUR MasterCard P1
+ 1.50 CHF
+ 19.00 EUR Dépenses
+ 1.50 CHF Frais:Gestion Comptes
+ 19.00 EUR Nourriture
+ 670.60 CHF Equity:Opening Balances
+ -18.00 EUR Recettes:Erreurs
+--------------------
+ 0
+end test
diff --git a/test/regress/1703.test b/test/regress/1703.test
new file mode 100644
index 00000000..983b79be
--- /dev/null
+++ b/test/regress/1703.test
@@ -0,0 +1,11 @@
+
+P 2018-10-31 MultifundosPlus R$0
+
+2017-05-03 * Test
+ Assets:A 1 AAA @ R$ 3000
+ Assets:B
+
+test reg assets:a -V --now 2018-12-31
+17-May-03 Test Assets:A R$3000 R$3000
+end test
+
diff --git a/test/regress/1722.test b/test/regress/1722.test
new file mode 100644
index 00000000..432a19b2
--- /dev/null
+++ b/test/regress/1722.test
@@ -0,0 +1,12 @@
+
+2003/12/20 Organic Co-op
+ Expenses:Food:Groceries $ 37.50 ; ] [=2004/01/01]
+ Assets:Cash $-37.50
+
+test bal
+ $ -37.50 Assets:Cash
+ $ 37.50 Expenses:Food:Groceries
+--------------------
+ 0
+end test
+
diff --git a/test/regress/1723.test b/test/regress/1723.test
new file mode 100644
index 00000000..62a50386
--- /dev/null
+++ b/test/regress/1723.test
@@ -0,0 +1,5 @@
+2017/3/17 deferred posting
+ <deferred posting>
+
+test reg
+end test
diff --git a/test/garbage-input.dat b/test/regress/25A099C9.dat
index 1b7d2101..1b7d2101 100644
--- a/test/garbage-input.dat
+++ b/test/regress/25A099C9.dat
diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test
index cfc0eabd..36776520 100644
--- a/test/regress/25A099C9.test
+++ b/test/regress/25A099C9.test
@@ -1,61 +1,61 @@
-test -f test/garbage-input.dat reg -> 29
+test -f test/regress/25A099C9.dat reg -> 29
__ERROR__
-While parsing file "$sourcepath/test/garbage-input.dat", line 1:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 1:
Error: Directive '/*' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 32:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 32:
Error: Directive '/**' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 36:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 36:
Error: Directive '/**' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 66:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 66:
Error: No quantity specified for amount
-While parsing file "$sourcepath/test/garbage-input.dat", line 69:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 69:
Error: Unexpected whitespace at beginning of line
-While parsing file "$sourcepath/test/garbage-input.dat", line 78:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 78:
Error: Directive '};' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 82:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 82:
Error: Directive '/**' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 93:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 93:
Error: Unexpected whitespace at beginning of line
-While parsing file "$sourcepath/test/garbage-input.dat", line 97:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 97:
Error: Directive '{' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 98:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 98:
Error: Directive 'public:' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 120:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 120:
Error: Directive 'protected:' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 131:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 131:
Error: Directive 'public:' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 711:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 711:
Error: Unexpected whitespace at beginning of line
-While parsing file "$sourcepath/test/garbage-input.dat", line 740:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 740:
Error: Directive 'private:' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 749:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 749:
Error: Unexpected whitespace at beginning of line
-While parsing file "$sourcepath/test/garbage-input.dat", line 750:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 750:
Error: Directive '};' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 752:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 752:
Error: Invalid date/time: line amount_t amoun
-While parsing file "$sourcepath/test/garbage-input.dat", line 756:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 756:
Error: Directive '}' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 758:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 758:
Error: Invalid date/time: line string amount_
-While parsing file "$sourcepath/test/garbage-input.dat", line 762:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 762:
Error: Directive '}' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 764:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 764:
Error: Invalid date/time: line string amount_
-While parsing file "$sourcepath/test/garbage-input.dat", line 768:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 768:
Error: Directive '}' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 770:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 770:
Error: Invalid date/time: line string amount_
-While parsing file "$sourcepath/test/garbage-input.dat", line 774:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 774:
Error: Directive '}' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 776:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 776:
Error: Invalid date/time: line std::ostream&
-While parsing file "$sourcepath/test/garbage-input.dat", line 782:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 782:
Error: Directive '}' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 783:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 783:
Error: Invalid date/time: line std::istream&
-While parsing file "$sourcepath/test/garbage-input.dat", line 786:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 786:
Error: Directive '}' requires an argument
-While parsing file "$sourcepath/test/garbage-input.dat", line 789:
+While parsing file "$sourcepath/test/regress/25A099C9.dat", line 789:
Error: Unexpected whitespace at beginning of line
end test
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 9cacb4e7..3611b00a 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -20,7 +20,4 @@ if (BUILD_LIBRARY)
target_link_libraries(MathTests ${PYTHON_LIBRARIES})
endif()
add_ledger_test(MathTests)
-
- set_target_properties(check PROPERTIES DEPENDS LedgerUtilTests)
- set_target_properties(check PROPERTIES DEPENDS LedgerMathTests)
endif()
diff --git a/tools/travis-before_install.sh b/tools/travis-before_install.sh
deleted file mode 100755
index fe010945..00000000
--- a/tools/travis-before_install.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-#set -x
-set -e
-set -o pipefail
-
-if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
- brew update
-fi
-
-if [ -n "${BOOST_VERSION}" ]; then
- mkdir -p $BOOST_ROOT
- wget --no-verbose --output-document=- \
- http://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION}/boost_${BOOST_VERSION//./_}.tar.bz2/download \
- | tar jxf - --strip-components=1 -C "${BOOST_ROOT}"
-fi
diff --git a/tools/travis-install.sh b/tools/travis-install.sh
deleted file mode 100755
index 4e8bdc48..00000000
--- a/tools/travis-install.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-#set -x
-set -e
-set -o pipefail
-
-if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
- for formula in $(echo "${BREWS//,/ }"); do
- echo "Checking ${formula} formula"
- brew outdated "${formula}" \
- || (brew unlink "${formula}"
- brew install "${formula}"
- )
- done
-fi
-
-if [ -d "${BOOST_ROOT}" ]; then
- (cd "${BOOST_ROOT}"
- ./bootstrap.sh --with-libraries="${BOOST_LIBS}"
- ./b2 threading=multi --prefix="${BOOST_ROOT}" -d0 install
- )
-fi
diff --git a/tools/update_copyright_year.sh b/tools/update_copyright_year.sh
index db9541d4..ab08a947 100755
--- a/tools/update_copyright_year.sh
+++ b/tools/update_copyright_year.sh
@@ -5,7 +5,7 @@
# This script will replace the last year of Copyright statements with the first
# argument of this script (defaulting to the current year).
-# Copyright (c) 2016 Alexis Hildebrandt
+# Copyright (c) 2016, 2019 Alexis Hildebrandt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -31,6 +31,6 @@ YEAR=${1:-$(date +%Y)}
GREP=${2:-egrep}
${GREP} -Rl 'Copyright.*Wiegley' $(git ls-files | cut -d / -f1 | uniq) \
- | ${GREP} -v "(test/garbage-input.dat|$(basename $0))" \
+ | ${GREP} -v "(test/regress/25A099C9.dat|$(basename $0))" \
| xargs sed -i '' -e "s/\(Copyright.*\)-20[0-9]\{2\}/\1-${YEAR}/"