Merge commit '3c2d228ddc74b75122b07a87bbd06263092a9661' into next

This commit is contained in:
Craig Earls 2015-08-06 19:38:39 -07:00
commit 0e82bd0d63
58 changed files with 2354 additions and 178 deletions

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "lib/utfcpp"]
path = lib/utfcpp
url = http://github.com/ledger/utfcpp.git

View file

@ -2,34 +2,106 @@
# 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 http://lint.travis-ci.org/
# or travis-yaml https://github.com/travis-ci/travis-yaml
# Travis WebLint https://lint.travis-ci.org/
# or travis-lint https://github.com/travis-ci/travis-lint
language: cpp
compiler:
- clang
- 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.58.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
addons:
coverity_scan:
project:
name: "ledger/ledger"
description: "Build submitted via Travis CI"
build_command_prepend: "cmake . -DUSE_PYTHON=ON -DBUILD_DEBUG=ON -DCLANG_GCOV=ON"
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
before_install:
# Add software package repositories with recent versions of gcc and boost
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test --yes
- sudo add-apt-repository ppa:boost-latest/ppa --yes
- sudo apt-get update -qq
- 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
install:
# Install GNU GCC 4.8 required by use of C++11
- if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8 gcc-4.8; fi
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
# Install Ledger dependencies
- sudo apt-get install -qq libboost1.55 libgmp-dev libmpfr-dev libeditline-dev
install:
- tools/travis-install.sh
before_script:
- ./acprep debug make --python
- cmake . -DUSE_PYTHON=ON -DBUILD_DEBUG=ON
- make
script:
- ./acprep check --jobs $(nproc) -- --output-on-failure
- 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

View file

@ -12,6 +12,9 @@ set(Ledger_VERSION_PATCH 1)
set(Ledger_VERSION_PRERELEASE "-alpha.1")
set(Ledger_VERSION_DATE 20141005)
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
enable_testing()
add_definitions(-std=c++11)
@ -225,7 +228,7 @@ macro(add_ledger_library_dependencies _target)
target_link_libraries(${_target} ${INTL_LIB})
endif()
if (HAVE_BOOST_PYTHON)
if(APPLE)
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
# Don't link directly to a Python framework on OS X, to avoid segfaults
# when the module is imported from a different interpreter
target_link_libraries(${_target} ${Boost_LIBRARIES})
@ -244,13 +247,20 @@ endmacro(add_ledger_library_dependencies _target)
########################################################################
include(FindUtfcpp)
if (UTFCPP_FOUND)
include_directories("${UTFCPP_INCLUDE_DIR}")
else()
message(FATAL_ERROR "Missing required header file: utf8.h\n"
"Define UTFCPP_PATH or install utfcpp locally into the source tree below lib/utfcpp/."
)
endif()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# add the binary tree to the search path for include files so that we will
# find TutorialConfig.h
include_directories("${PROJECT_SOURCE_DIR}/lib")
include_directories("${PROJECT_SOURCE_DIR}/lib/utfcpp/source")
# find system.hh
include_directories("${PROJECT_BINARY_DIR}")
configure_file(

View file

@ -1,7 +1,8 @@
[![Build Status](https://img.shields.io/travis/ledger/ledger/master.svg?&style=flat)](https://travis-ci.org/ledger/ledger)
[![Build Status master](https://img.shields.io/travis/ledger/ledger/master.svg?label=master&style=flat)](https://travis-ci.org/ledger/ledger)
[![Build Status next](https://img.shields.io/travis/ledger/ledger/next.svg?label=next&style=flat)](https://travis-ci.org/ledger/ledger)
[![Status](https://img.shields.io/badge/status-active-brightgreen.svg?style=flat)](https://github.com/ledger/ledger/pulse/monthly)
[![License](https://img.shields.io/badge/license-BSD-blue.svg?style=flat)](http://opensource.org/licenses/BSD-3-Clause)
[![GitHub tag](https://img.shields.io/github/tag/ledger/ledger.svg?style=flat)](https://github.com/ledger/ledger/releases)
[![GitHub release](https://img.shields.io/github/release/ledger/ledger.svg?style=flat)](https://github.com/ledger/ledger/releases)
# Ledger: Command-Line Accounting
@ -147,9 +148,9 @@ Or, for Ubuntu 12.04:
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) and Debian 8 (jessie) contain all components needed to
build ledger. You can install all required build dependencies using the
following command:
Debian 7 (wheezy), Debian 8 (jessie), Debian testing (stretch) 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 \

10
acprep
View file

@ -490,17 +490,10 @@ class PrepareBuild(CommandLineApp):
# Update local files with the latest information #
#########################################################################
def phase_submodule(self, *args):
self.log.info('Executing phase: submodule')
if self.git_working_tree():
self.execute('git', 'submodule', 'init')
self.execute('git', 'submodule', 'update')
def phase_pull(self, *args):
self.log.info('Executing phase: pull')
if self.git_working_tree():
self.execute('git', 'pull')
self.phase_submodule()
#########################################################################
# Automatic installation of build dependencies #
@ -569,6 +562,7 @@ class PrepareBuild(CommandLineApp):
'libedit-dev',
'texinfo',
'lcov',
'libutfcpp-dev',
'sloccount'
] + BoostInfo().dependencies('ubuntu-trusty')
elif re.search('saucy', info):
@ -606,6 +600,7 @@ class PrepareBuild(CommandLineApp):
'libedit-dev',
'texinfo',
'lcov',
'libutfcpp-dev',
'sloccount'
] + BoostInfo().dependencies('ubuntu-precise')
else:
@ -885,7 +880,6 @@ class PrepareBuild(CommandLineApp):
def phase_config(self, *args):
self.log.info('Executing phase: config')
self.phase_submodule()
self.phase_configure(*args)
if self.should_clean:
self.phase_clean()

30
cmake/FindUtfcpp.cmake Normal file
View file

@ -0,0 +1,30 @@
# - Try to find utfcpp
# Once done, this will define
#
# UTFCPP_FOUND - system has utfcpp's utf8.h
# UTFCPP_PATH - the utfcpp include directories
include(CheckCXXSourceCompiles)
set(UTFCPP_FOUND FALSE)
find_path(UTFCPP_INCLUDE_DIR
NAMES utf8.h
HINTS "${UTFCPP_PATH}" "${PROJECT_SOURCE_DIR}/lib/utfcpp/v2_0/source"
)
if (UTFCPP_INCLUDE_DIR)
set(CMAKE_REQUIRED_INCLUDES "${UTFCPP_INCLUDE_DIR}")
set(UTFCPP_FOUND TRUE)
endif()
check_cxx_source_compiles("
#include <string>
#include \"utf8.h\"
int main(int argc, char** argv) {
std::string input = std::string(\"utfcpp\");
const char * p = input.c_str();
std::size_t len = input.length();
utf8::is_valid(p, p + len);
}" HAVE_WORKING_UTFCPP)

View file

@ -1,5 +1,29 @@
Ledger NEWS
* 3.x.x
- Added a --no-revalued option
- Improved Embedded Python Support
- Fixed parsing of transactions with single-character payees and comments
- Fixed crash when using -M with empty result
- Fixed sorting for option --auto-match
- Fixed treatment of "year 2015" and "Y2014" directives
- Fixed crash when using --trace 10 or above
- Build fix for boost 1.56
- Build fix for Cygwin
- Fixed Util and Math tests on Mac OS X
- Various documentation improvements
* 3.1
- Changed the definition of cost basis to preserve the original cost basis

View file

@ -574,7 +574,12 @@ posting occurring in that period.
Display values in terms of the given
.Ar COMMODITY .
The latest available price is used.
.\".It Fl \-explicit
.It Fl \-explicit
Direct
.Nm
to require pre-declarations for entities (such as accounts,
commodities and tags) rather than taking entities from cleared
transactions as defined.
.It Fl \-file Ar FILE
Read journal data from
.Ar FILE .
@ -716,9 +721,15 @@ Aliases are completely ignored.
Suppress any color TTY output.
.It Fl \-no-pager
Disables the pager on TTY output.
.It Fl \-no-revalued
Stop
.Nm
from showing
<Revalued>
postings.
.It Fl \-no-rounding
Don't output
.Qq Li <Rounding>
.Qq Li <Adjustment>
postings. Note that this will cause the
running total to often not add up! Its main use is for
.Fl \-amount-data Pq Fl j

View file

@ -2105,6 +2105,7 @@ the syntax @code{[ACTUAL_DATE]} or @code{[=EFFECTIVE_DATE]} or
@item P
@findex --download
@findex P
@cindex historical prices
Specifies a historical price for a commodity. These are usually found
in a pricing history file (see the @option{--download (-Q)} option).
The syntax is:
@ -2114,6 +2115,9 @@ P DATE SYMBOL PRICE
@end smallexample
@item =
@findex =
@cindex automated transaction
@cindex transaction, automated
An automated transaction. A value expression must appear after the
equal sign.
@ -2124,17 +2128,23 @@ posting is matched by the value expression (@pxref{Automated
Transactions}).
@item ~
@findex ~
@cindex periodic transaction
@cindex transaction, periodic
A periodic transaction. A period expression must appear after the tilde.
After this initial line there should be a set of one or more
postings, just as if it were a normal transaction.
@item ; # % | *
@findex comment
@cindex comments
A line beginning with a semicolon, pound, percent, bar or asterisk
indicates a comment, and is ignored. Comments will not be returned in
a ``print'' response.
@item indented ;
@cindex tags
If the semicolon is indented and occurs inside a transaction, it is
parsed as a persistent note for its preceding category. These notes or
tags can be used to augment the reporting and filtering capabilities of
@ -2154,6 +2164,8 @@ Command directives must occur at the beginning of a line. Use of
@samp{!} and @samp{@@} is deprecated.
@item account
@findex account
@cindex pre-declare account
Pre-declare valid account names. This only has an effect if
@option{--strict} or @option{--pedantic} is used (see below). The
@code{account} directive supports several optional sub-directives, if
@ -2203,6 +2215,7 @@ used as the ``balancing account'' for any future transactions that
contain only a single posting.
@item apply account
@findex apply account
@c instance_t::master_account_directive
Sets the root for all accounts following this directive. Ledger
supports a hierarchical tree of accounts. It may be convenient to
@ -2225,6 +2238,8 @@ Would result in all postings going into
until an @samp{end apply account} directive was found.
@item alias
@findex alias
@cindex account, alias
@c instance_t::alias_directive
Define an alias for an account name. If you have a deeply nested tree
of accounts, it may be convenient to define an alias, for example:
@ -2276,6 +2291,8 @@ The option @option{--no-aliases} completely disables alias expansion.
All accounts are read verbatim as they are in the ledger file.
@item assert
@findex assert
@cindex assertions
@c instance_t::assert_directive
An assertion can throw an error if a condition is not met during
Ledger's run.
@ -2286,6 +2303,8 @@ assert <VALUE EXPRESSION BOOLEAN RESULT>
@item bucket
@anchor{bucket}
@findex bucket
@cindex bucket
@c instance_t::default_account_directive
Defines the default account to use for balancing transactions.
Normally, each transaction has at least two postings, which must
@ -2311,6 +2330,7 @@ bucket Assets:Checking
@item capture
@c instance_t::account_mapping_directive
@findex capture
@findex print
@findex register
@ -2328,6 +2348,8 @@ Ledger will display the mapped payees in @command{print} and
@command{register} reports.
@item check
@findex check
@cindex assertions
@c instance_t::check_directive in textual.cc
A check issues a warning if a condition is not met during Ledger's
run.
@ -2337,10 +2359,14 @@ check <VALUE EXPRESSION BOOLEAN RESULT>
@end smallexample
@item comment
@findex comment
@cindex comments
@c instance_t::comment_directive in textual.cc
Start a block comment, closed by @code{end comment}.
@item commodity
@findex commodity
@cindex pre-declare commodity
Pre-declare commodity names. This only has an effect if
@option{--strict} or @option{--pedantic} is used (see below).
@ -2375,6 +2401,7 @@ should never be auto-downloaded.
The @code{default} sub-directive marks this as the ``default'' commodity.
@item define
@findex define
@c instance_t::define_directive in textual.cc
Allows you to define value expressions for future use. For example:
@ -2389,13 +2416,17 @@ define var_name=$100
The posting will have a cost of $400.
@item end
@findex end
@c instance_t::end_directive in textual.cc
Closes block commands like @code{tag} or @code{comment}.
@item expr
@findex expr
@c instance_t::expr_directive in textual.cc
@item fixed
@findex fixed
@cindex fixated prices
@c instance_t::fixed_directive in textual.cc
A fixed block is used to set fixated prices (@pxref{Fixated prices and
@ -2437,10 +2468,12 @@ For the moment, users may wish to study
before using the @code{fixed} directive in production.
@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.
@item payee
@findex payee
@c instance_t::payee_alias_mapping_directive in textual.cc
@c instance_t::payee_uuid_mapping_directive in textual.cc
@findex print
@ -2475,6 +2508,7 @@ Ledger will display the mapped payees in @command{print} and
@command{register} reports.
@item apply tag
@findex apply tag
@c instance_t::tag_directive in textual.cc
Allows you to designate a block of transactions and assign the same
tag to all. Tags can have values and may be nested.
@ -2532,6 +2566,8 @@ is the equivalent of:
@c track.
@item tag
@findex tag
@cindex pre-declare tag
Pre-declares tag names. This only has an effect if @option{--strict} or
@option{--pedantic} is used (see below).
@ -2558,11 +2594,14 @@ but a string if typed metadata is used!). Such checks or assertions are
not called if no value is given.
@item test
@findex test
@cindex comments
@c instance_t::comment_directive in textual.cc
This is a synonym for @code{comment} and must be closed by an
@code{end} tag.
@item year
@findex year
@anchor{year}
@c instance_t::year_directive in textual.cc
Denotes the year used for all subsequent transactions that give a date
@ -2580,10 +2619,12 @@ alone, for backwards compatibility with older Ledger versions.
@item A
@findex A
@findex bucket
@xref{bucket}.
@item Y
@findex Y
@findex year
@xref{year}.
@item N SYMBOL
@ -2601,7 +2642,6 @@ N SYMBOL
@item D AMOUNT
@findex xact
@findex D
Specifies the default commodity to use, by specifying an amount in the
expected format. The @command{xact} command will use this commodity as
the default when none other can be determined. This command may be used
@ -5970,8 +6010,10 @@ Direct Ledger to download prices.
@c @option{--getquote @var{FILE}}.
@item --explicit
@c see test/baseline/opt-explicit.test
@value{FIXME:UNDOCUMENTED}
Direct Ledger to require pre-declarations for entities (such as accounts,
commodities and tags) rather than taking entities from cleared
transactions as defined. This option is useful in combination with
@option{--strict} or @option{--pedantic}.
@item --file @var{FILE}
@itemx -f @var{FILE}
@ -6536,8 +6578,12 @@ Suppress any color TTY output.
@item --no-pager
Direct output to stdout, avoiding pager program.
@item --no-revalued
Stop Ledger from showing @code{<Revalued>} postings. This option is useful
in combination with the @option{--exchange} or @option{--market} option.
@item --no-rounding
Don't output @samp{<Rounding>} postings. Note that this will cause the
Don't output @samp{<Adjustment>} postings. Note that this will cause the
running total to often not add up! Its main use is for
@option{--amount-data (-j)} and @option{--total-data (-J)} reports.
@ -7868,6 +7914,13 @@ considering children.
The cost of a posting; the cost of an account, without its
children.
@item v
The market value of a posting or an account, without its children.
@item g
The net gain (market value minus cost basis), for a posting or an
account, without its children. It is the same as @samp{v-b}.
@item l
The depth (``level'') of an account. If an account has one parent,
its depth is one.
@ -8107,7 +8160,7 @@ Render the given @var{expression} as a string, applying the proper ANSI escape
codes to display it in the given @var{color} if @var{bool} is true. It
typically checks the value of the option @option{--color}. Since ANSI escape
codes include non-printable character sequences, such as escape @kbd{^[}
the following example may not appear as the final result on the commandline.
the following example may not appear as the final result on the command-line.
@smallexample @c command:4D836EE,with_input:3406FC1
$ ledger -f expr.dat --format "%(ansify_if(account, blue, options.color))\n" reg
@end smallexample

@ -1 +0,0 @@
Subproject commit 2233ec933f5661c7050b94d3b14f5f9f51ae3d55

View file

@ -1,4 +1,4 @@
Copyright 2006 Nemanja Trifunovic
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by

18
lib/utfcpp/v2_0/buildrelease.pl Executable file
View file

@ -0,0 +1,18 @@
#! /usr/bin/perl
$release_files = 'source/utf8.h source/utf8/core.h source/utf8/checked.h source/utf8/unchecked.h doc/utf8cpp.html doc/ReleaseNotes';
# First get the latest version
`svn update`;
# Then construct the name of the zip file
$argc = @ARGV;
if ($argc > 0) {
$zip_name = $ARGV[0];
}
else {
$zip_name = "utf8";
}
# Zip the files to an archive
`zip $zip_name $release_files`;

View file

@ -0,0 +1,5 @@
CC = g++
CFLAGS = -g -Wall -pedantic
docsample: docsample.cpp ../source/utf8.h
$(CC) $(CFLAGS) docsample.cpp -odocsample

View file

@ -0,0 +1,52 @@
#include "../source/utf8.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char** argv)
{
if (argc != 2) {
cout << "\nUsage: docsample filename\n";
return 0;
}
const char* test_file_path = argv[1];
// Open the test file (must be UTF-8 encoded)
ifstream fs8(test_file_path);
if (!fs8.is_open()) {
cout << "Could not open " << test_file_path << endl;
return 0;
}
unsigned line_count = 1;
string line;
// Play with all the lines in the file
while (getline(fs8, line)) {
// check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)
string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
if (end_it != line.end()) {
cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n";
cout << "This part is fine: " << string(line.begin(), end_it) << "\n";
}
// Get the line length (at least for the valid part)
int length = utf8::distance(line.begin(), end_it);
cout << "Length of line " << line_count << " is " << length << "\n";
// Convert it to utf-16
vector<unsigned short> utf16line;
utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
// And back to utf-8;
string utf8line;
utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
// Confirm that the conversion went OK:
if (utf8line != string(line.begin(), end_it))
cout << "Error in UTF-16 conversion at line: " << line_count << "\n";
line_count++;
}
return 0;
}

View file

@ -0,0 +1,34 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "utf8/checked.h"
#include "utf8/unchecked.h"
#endif // header guard

View file

@ -0,0 +1,327 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "core.h"
#include <stdexcept>
namespace utf8
{
// Base for the exceptions that may be thrown from the library
class exception : public ::std::exception {
};
// Exceptions that may be thrown from the library functions.
class invalid_code_point : public exception {
uint32_t cp;
public:
invalid_code_point(uint32_t cp) : cp(cp) {}
virtual const char* what() const throw() { return "Invalid code point"; }
uint32_t code_point() const {return cp;}
};
class invalid_utf8 : public exception {
uint8_t u8;
public:
invalid_utf8 (uint8_t u) : u8(u) {}
virtual const char* what() const throw() { return "Invalid UTF-8"; }
uint8_t utf8_octet() const {return u8;}
};
class invalid_utf16 : public exception {
uint16_t u16;
public:
invalid_utf16 (uint16_t u) : u16(u) {}
virtual const char* what() const throw() { return "Invalid UTF-16"; }
uint16_t utf16_word() const {return u16;}
};
class not_enough_room : public exception {
public:
virtual const char* what() const throw() { return "Not enough space"; }
};
/// The library API - functions intended to be called by the users
template <typename octet_iterator>
octet_iterator append(uint32_t cp, octet_iterator result)
{
if (!utf8::internal::is_code_point_valid(cp))
throw invalid_code_point(cp);
if (cp < 0x80) // one octet
*(result++) = static_cast<uint8_t>(cp);
else if (cp < 0x800) { // two octets
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else if (cp < 0x10000) { // three octets
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else { // four octets
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
return result;
}
template <typename octet_iterator, typename output_iterator>
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
{
while (start != end) {
octet_iterator sequence_start = start;
internal::utf_error err_code = utf8::internal::validate_next(start, end);
switch (err_code) {
case internal::UTF8_OK :
for (octet_iterator it = sequence_start; it != start; ++it)
*out++ = *it;
break;
case internal::NOT_ENOUGH_ROOM:
throw not_enough_room();
case internal::INVALID_LEAD:
out = utf8::append (replacement, out);
++start;
break;
case internal::INCOMPLETE_SEQUENCE:
case internal::OVERLONG_SEQUENCE:
case internal::INVALID_CODE_POINT:
out = utf8::append (replacement, out);
++start;
// just one replacement mark for the sequence
while (start != end && utf8::internal::is_trail(*start))
++start;
break;
}
}
return out;
}
template <typename octet_iterator, typename output_iterator>
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
{
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
return utf8::replace_invalid(start, end, out, replacement_marker);
}
template <typename octet_iterator>
uint32_t next(octet_iterator& it, octet_iterator end)
{
uint32_t cp = 0;
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
switch (err_code) {
case internal::UTF8_OK :
break;
case internal::NOT_ENOUGH_ROOM :
throw not_enough_room();
case internal::INVALID_LEAD :
case internal::INCOMPLETE_SEQUENCE :
case internal::OVERLONG_SEQUENCE :
throw invalid_utf8(*it);
case internal::INVALID_CODE_POINT :
throw invalid_code_point(cp);
}
return cp;
}
template <typename octet_iterator>
uint32_t peek_next(octet_iterator it, octet_iterator end)
{
return utf8::next(it, end);
}
template <typename octet_iterator>
uint32_t prior(octet_iterator& it, octet_iterator start)
{
// can't do much if it == start
if (it == start)
throw not_enough_room();
octet_iterator end = it;
// Go back until we hit either a lead octet or start
while (utf8::internal::is_trail(*(--it)))
if (it == start)
throw invalid_utf8(*it); // error - no lead byte in the sequence
return utf8::peek_next(it, end);
}
/// Deprecated in versions that include "prior"
template <typename octet_iterator>
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
{
octet_iterator end = it;
while (utf8::internal::is_trail(*(--it)))
if (it == pass_start)
throw invalid_utf8(*it); // error - no lead byte in the sequence
octet_iterator temp = it;
return utf8::next(temp, end);
}
template <typename octet_iterator, typename distance_type>
void advance (octet_iterator& it, distance_type n, octet_iterator end)
{
for (distance_type i = 0; i < n; ++i)
utf8::next(it, end);
}
template <typename octet_iterator>
typename std::iterator_traits<octet_iterator>::difference_type
distance (octet_iterator first, octet_iterator last)
{
typename std::iterator_traits<octet_iterator>::difference_type dist;
for (dist = 0; first < last; ++dist)
utf8::next(first, last);
return dist;
}
template <typename u16bit_iterator, typename octet_iterator>
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
{
while (start != end) {
uint32_t cp = utf8::internal::mask16(*start++);
// Take care of surrogate pairs first
if (utf8::internal::is_lead_surrogate(cp)) {
if (start != end) {
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
if (utf8::internal::is_trail_surrogate(trail_surrogate))
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
else
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
}
else
throw invalid_utf16(static_cast<uint16_t>(cp));
}
// Lone trail surrogate
else if (utf8::internal::is_trail_surrogate(cp))
throw invalid_utf16(static_cast<uint16_t>(cp));
result = utf8::append(cp, result);
}
return result;
}
template <typename u16bit_iterator, typename octet_iterator>
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
{
while (start != end) {
uint32_t cp = utf8::next(start, end);
if (cp > 0xffff) { //make a surrogate pair
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
}
else
*result++ = static_cast<uint16_t>(cp);
}
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
{
while (start != end)
result = utf8::append(*(start++), result);
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
{
while (start != end)
(*result++) = utf8::next(start, end);
return result;
}
// The iterator class
template <typename octet_iterator>
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
octet_iterator it;
octet_iterator range_start;
octet_iterator range_end;
public:
iterator () {}
explicit iterator (const octet_iterator& octet_it,
const octet_iterator& range_start,
const octet_iterator& range_end) :
it(octet_it), range_start(range_start), range_end(range_end)
{
if (it < range_start || it > range_end)
throw std::out_of_range("Invalid utf-8 iterator position");
}
// the default "big three" are OK
octet_iterator base () const { return it; }
uint32_t operator * () const
{
octet_iterator temp = it;
return utf8::next(temp, range_end);
}
bool operator == (const iterator& rhs) const
{
if (range_start != rhs.range_start || range_end != rhs.range_end)
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
return (it == rhs.it);
}
bool operator != (const iterator& rhs) const
{
return !(operator == (rhs));
}
iterator& operator ++ ()
{
utf8::next(it, range_end);
return *this;
}
iterator operator ++ (int)
{
iterator temp = *this;
utf8::next(it, range_end);
return temp;
}
iterator& operator -- ()
{
utf8::prior(it, range_start);
return *this;
}
iterator operator -- (int)
{
iterator temp = *this;
utf8::prior(it, range_start);
return temp;
}
}; // class iterator
} // namespace utf8
#endif //header guard

View file

@ -0,0 +1,329 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include <iterator>
namespace utf8
{
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
// You may need to change them to match your system.
// These typedefs have the same names as ones from cstdint, or boost/cstdint
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
// Helper code - not intended to be directly called by the library users. May be changed at any time
namespace internal
{
// Unicode constants
// Leading (high) surrogates: 0xd800 - 0xdbff
// Trailing (low) surrogates: 0xdc00 - 0xdfff
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
// Maximum valid value for a Unicode code point
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
template<typename octet_type>
inline uint8_t mask8(octet_type oc)
{
return static_cast<uint8_t>(0xff & oc);
}
template<typename u16_type>
inline uint16_t mask16(u16_type oc)
{
return static_cast<uint16_t>(0xffff & oc);
}
template<typename octet_type>
inline bool is_trail(octet_type oc)
{
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
}
template <typename u16>
inline bool is_lead_surrogate(u16 cp)
{
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
}
template <typename u16>
inline bool is_trail_surrogate(u16 cp)
{
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
}
template <typename u16>
inline bool is_surrogate(u16 cp)
{
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
}
template <typename u32>
inline bool is_code_point_valid(u32 cp)
{
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
}
template <typename octet_iterator>
inline typename std::iterator_traits<octet_iterator>::difference_type
sequence_length(octet_iterator lead_it)
{
uint8_t lead = utf8::internal::mask8(*lead_it);
if (lead < 0x80)
return 1;
else if ((lead >> 5) == 0x6)
return 2;
else if ((lead >> 4) == 0xe)
return 3;
else if ((lead >> 3) == 0x1e)
return 4;
else
return 0;
}
template <typename octet_difference_type>
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
{
if (cp < 0x80) {
if (length != 1)
return true;
}
else if (cp < 0x800) {
if (length != 2)
return true;
}
else if (cp < 0x10000) {
if (length != 3)
return true;
}
return false;
}
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
/// Helper for get_sequence_x
template <typename octet_iterator>
utf_error increase_safely(octet_iterator& it, octet_iterator end)
{
if (++it == end)
return NOT_ENOUGH_ROOM;
if (!utf8::internal::is_trail(*it))
return INCOMPLETE_SEQUENCE;
return UTF8_OK;
}
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
/// get_sequence_x functions decode utf-8 sequences of the length x
template <typename octet_iterator>
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (*it) & 0x3f;
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (*it) & 0x3f;
return UTF8_OK;
}
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
template <typename octet_iterator>
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
// Save the original value of it so we can go back in case of failure
// Of course, it does not make much sense with i.e. stream iterators
octet_iterator original_it = it;
uint32_t cp = 0;
// Determine the sequence length based on the lead octet
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
const octet_difference_type length = utf8::internal::sequence_length(it);
// Get trail octets and calculate the code point
utf_error err = UTF8_OK;
switch (length) {
case 0:
return INVALID_LEAD;
case 1:
err = utf8::internal::get_sequence_1(it, end, cp);
break;
case 2:
err = utf8::internal::get_sequence_2(it, end, cp);
break;
case 3:
err = utf8::internal::get_sequence_3(it, end, cp);
break;
case 4:
err = utf8::internal::get_sequence_4(it, end, cp);
break;
}
if (err == UTF8_OK) {
// Decoding succeeded. Now, security checks...
if (utf8::internal::is_code_point_valid(cp)) {
if (!utf8::internal::is_overlong_sequence(cp, length)){
// Passed! Return here.
code_point = cp;
++it;
return UTF8_OK;
}
else
err = OVERLONG_SEQUENCE;
}
else
err = INVALID_CODE_POINT;
}
// Failure branch - restore the original value of the iterator
it = original_it;
return err;
}
template <typename octet_iterator>
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
uint32_t ignored;
return utf8::internal::validate_next(it, end, ignored);
}
} // namespace internal
/// The library API - functions intended to be called by the users
// Byte order mark
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
template <typename octet_iterator>
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
{
octet_iterator result = start;
while (result != end) {
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
if (err_code != internal::UTF8_OK)
return result;
}
return result;
}
template <typename octet_iterator>
inline bool is_valid(octet_iterator start, octet_iterator end)
{
return (utf8::find_invalid(start, end) == end);
}
template <typename octet_iterator>
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
{
return (
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
);
}
//Deprecated in release 2.3
template <typename octet_iterator>
inline bool is_bom (octet_iterator it)
{
return (
(utf8::internal::mask8(*it++)) == bom[0] &&
(utf8::internal::mask8(*it++)) == bom[1] &&
(utf8::internal::mask8(*it)) == bom[2]
);
}
} // namespace utf8
#endif // header guard

View file

@ -0,0 +1,228 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "core.h"
namespace utf8
{
namespace unchecked
{
template <typename octet_iterator>
octet_iterator append(uint32_t cp, octet_iterator result)
{
if (cp < 0x80) // one octet
*(result++) = static_cast<uint8_t>(cp);
else if (cp < 0x800) { // two octets
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else if (cp < 0x10000) { // three octets
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else { // four octets
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
return result;
}
template <typename octet_iterator>
uint32_t next(octet_iterator& it)
{
uint32_t cp = utf8::internal::mask8(*it);
typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
switch (length) {
case 1:
break;
case 2:
it++;
cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
break;
case 3:
++it;
cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
++it;
cp += (*it) & 0x3f;
break;
case 4:
++it;
cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
++it;
cp += (utf8::internal::mask8(*it) << 6) & 0xfff;
++it;
cp += (*it) & 0x3f;
break;
}
++it;
return cp;
}
template <typename octet_iterator>
uint32_t peek_next(octet_iterator it)
{
return utf8::unchecked::next(it);
}
template <typename octet_iterator>
uint32_t prior(octet_iterator& it)
{
while (utf8::internal::is_trail(*(--it))) ;
octet_iterator temp = it;
return utf8::unchecked::next(temp);
}
// Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)
template <typename octet_iterator>
inline uint32_t previous(octet_iterator& it)
{
return utf8::unchecked::prior(it);
}
template <typename octet_iterator, typename distance_type>
void advance (octet_iterator& it, distance_type n)
{
for (distance_type i = 0; i < n; ++i)
utf8::unchecked::next(it);
}
template <typename octet_iterator>
typename std::iterator_traits<octet_iterator>::difference_type
distance (octet_iterator first, octet_iterator last)
{
typename std::iterator_traits<octet_iterator>::difference_type dist;
for (dist = 0; first < last; ++dist)
utf8::unchecked::next(first);
return dist;
}
template <typename u16bit_iterator, typename octet_iterator>
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
{
while (start != end) {
uint32_t cp = utf8::internal::mask16(*start++);
// Take care of surrogate pairs first
if (utf8::internal::is_lead_surrogate(cp)) {
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
}
result = utf8::unchecked::append(cp, result);
}
return result;
}
template <typename u16bit_iterator, typename octet_iterator>
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
{
while (start < end) {
uint32_t cp = utf8::unchecked::next(start);
if (cp > 0xffff) { //make a surrogate pair
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
}
else
*result++ = static_cast<uint16_t>(cp);
}
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
{
while (start != end)
result = utf8::unchecked::append(*(start++), result);
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
{
while (start < end)
(*result++) = utf8::unchecked::next(start);
return result;
}
// The iterator class
template <typename octet_iterator>
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
octet_iterator it;
public:
iterator () {}
explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
// the default "big three" are OK
octet_iterator base () const { return it; }
uint32_t operator * () const
{
octet_iterator temp = it;
return utf8::unchecked::next(temp);
}
bool operator == (const iterator& rhs) const
{
return (it == rhs.it);
}
bool operator != (const iterator& rhs) const
{
return !(operator == (rhs));
}
iterator& operator ++ ()
{
::std::advance(it, utf8::internal::sequence_length(it));
return *this;
}
iterator operator ++ (int)
{
iterator temp = *this;
::std::advance(it, utf8::internal::sequence_length(it));
return temp;
}
iterator& operator -- ()
{
utf8::unchecked::prior(it);
return *this;
}
iterator operator -- (int)
{
iterator temp = *this;
utf8::unchecked::prior(it);
return temp;
}
}; // class iterator
} // namespace utf8::unchecked
} // namespace utf8
#endif // header guard

View file

@ -152,7 +152,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
-Wno-unused-parameter
-Wno-c++98-compat
-fno-limit-debug-info)
macro(ADD_PCH_RULE _header_filename _src_list _other_srcs)
set(_pch_filename "${_header_filename}.pch")
@ -188,7 +188,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args}
DEPENDS ${_header_filename})
endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs)
elseif(CMAKE_CXX_COMPILER MATCHES "g\\+\\+")
set(GXX_WARNING_FLAGS
-pedantic
@ -210,7 +210,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
-Wno-strict-aliasing)
add_definitions(${GXX_WARNING_FLAGS})
macro(ADD_PCH_RULE _header_filename _src_list _other_srcs)
set(_gch_filename "${_header_filename}.gch")
@ -247,7 +247,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args}
DEPENDS ${_header_filename})
endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs)
else()
macro(ADD_PCH_RULE _header_filename _src_list _other_srcs)
endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs)
@ -273,6 +273,9 @@ if (BUILD_LIBRARY)
add_executable(ledger main.cc global.cc)
target_link_libraries(ledger libledger)
if (CMAKE_SYSTEM_NAME STREQUAL Darwin AND HAVE_BOOST_PYTHON)
target_link_libraries(ledger ${PYTHON_LIBRARIES})
endif()
install(TARGETS libledger DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES ${LEDGER_INCLUDES}
@ -294,7 +297,7 @@ print(s.get_python_lib(True, prefix=''))"
if (PYTHON_SITE_PACKAGES)
if (WIN32 AND NOT CYGWIN)
set(_ledger_python_module_name "ledger.pyd")
elseif(CMAKE_HOST_APPLE)
elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(_ledger_python_module_name "ledger.so")
else()
set(_ledger_python_module_name "ledger${CMAKE_SHARED_LIBRARY_SUFFIX}")

View file

@ -261,11 +261,7 @@ public:
mutable optional<xdata_t> xdata_;
bool has_xdata() const {
#if BOOST_VERSION >= 105600
return xdata_ != NULL;
#else
return xdata_;
#endif
return static_cast<bool>(xdata_);
}
void clear_xdata();
xdata_t& xdata() {

View file

@ -72,7 +72,7 @@ public:
out.flush();
}
virtual void operator()(post_t& post);
virtual string escape_string(string raw);
virtual string escape_string(string raw);
};
} // namespace ledger

View file

@ -707,7 +707,7 @@ namespace {
insert_prices_in_map(price_map_t& _all_prices)
: all_prices(_all_prices) {}
void operator()(datetime_t& date, const amount_t& price) {
void operator()(const datetime_t& date, const amount_t& price) {
all_prices.insert(price_map_t::value_type(date, price));
}
};

View file

@ -475,7 +475,7 @@ commodity_history_impl_t::find_price(const commodity_t& source,
#endif
vertex_descriptor v = tv;
for (vertex_descriptor u = predecessorMap[v];
for (vertex_descriptor u = predecessorMap[v];
u != v;
v = u, u = predecessorMap[v])
{

View file

@ -174,11 +174,7 @@ public:
static bool use_aux_date;
virtual bool has_date() const {
#if BOOST_VERSION >= 105600
return _date != NULL;
#else
return _date;
#endif
return static_cast<bool>(_date);
}
virtual date_t date() const {

View file

@ -96,7 +96,7 @@ namespace {
TRACE_DTOR(create_price_xact);
}
void operator()(datetime_t& date, const amount_t& price) {
void operator()(const datetime_t& date, const amount_t& price) {
xact_t * xact;
string symbol = price.commodity().symbol();

View file

@ -118,7 +118,7 @@ public:
ptr_op_t parse(std::istream& in,
const parse_flags_t& flags = PARSE_DEFAULT,
const optional<string>& original_string = NULL);
const optional<string>& original_string = boost::none);
};
} // namespace ledger

View file

@ -78,8 +78,8 @@ public:
commodity_t * default_commodity;
bool keep_base; // --base
optional<path> price_db; // --price-db=
long quote_leeway; // --leeway=
optional<path> price_db; // --price-db=
long quote_leeway; // --leeway=
bool get_quotes; // --download
function<optional<price_point_t>

View file

@ -205,11 +205,7 @@ public:
mutable optional<xdata_t> xdata_;
bool has_xdata() const {
#if BOOST_VERSION >= 105600
return xdata_ != NULL;
#else
return xdata_;
#endif
return static_cast<bool>(xdata_);
}
void clear_xdata() {
xdata_ = none;

View file

@ -77,7 +77,7 @@ public:
if (name != "__main__")
main_module->define_global(name, mod->module_object);
return mod;
}
}
python_interpreter_t() : session_t(), is_initialized(false) {
TRACE_CTOR(python_interpreter_t, "");

View file

@ -396,7 +396,7 @@ value_t select_command(call_scope_t& args)
#if 0
query_t query;
keep_details_t keeper(true, true, true);
expr_t::ptr_op_t expr =
expr_t::ptr_op_t expr =
query.parse_args(string_value(arg).to_sequence(), keeper, false, true);
report.HANDLER(limit_).on("#select", query.get_query(query_t::QUERY_LIMIT));
#else

View file

@ -127,47 +127,14 @@ std::size_t session_t::read_data(const string& master_account)
if (HANDLED(value_expr_))
journal->value_expr = HANDLER(value_expr_).str();
if (price_db_path) {
if (exists(*price_db_path)) {
parsing_context.push(*price_db_path);
parsing_context.get_current().journal = journal.get();
try {
if (journal->read(parsing_context) > 0)
throw_(parse_error, _("Transactions not allowed in price history file"));
}
catch (...) {
parsing_context.pop();
throw;
}
parsing_context.pop();
}
}
foreach (const path& pathname, HANDLER(file_).data_files) {
if (pathname == "-" || pathname == "/dev/stdin") {
// To avoid problems with stdin and pipes, etc., we read the entire
// file in beforehand into a memory buffer, and then parcel it out
// from there.
std::ostringstream buffer;
while (std::cin.good() && ! std::cin.eof()) {
char line[8192];
std::cin.read(line, 8192);
std::streamsize count = std::cin.gcount();
buffer.write(line, count);
}
buffer.flush();
shared_ptr<std::istream> stream(new std::istringstream(buffer.str()));
parsing_context.push(stream);
} else {
parsing_context.push(pathname);
}
if (price_db_path) {
if (exists(*price_db_path)) {
parsing_context.push(*price_db_path);
parsing_context.get_current().journal = journal.get();
parsing_context.get_current().master = acct;
try {
xact_count += journal->read(parsing_context);
if (journal->read(parsing_context) > 0)
throw_(parse_error, _("Transactions not allowed in price history file"));
}
catch (...) {
parsing_context.pop();
@ -175,10 +142,44 @@ std::size_t session_t::read_data(const string& master_account)
}
parsing_context.pop();
}
}
DEBUG("ledger.read", "xact_count [" << xact_count
<< "] == journal->xacts.size() [" << journal->xacts.size() << "]");
assert(xact_count == journal->xacts.size());
foreach (const path& pathname, HANDLER(file_).data_files) {
if (pathname == "-" || pathname == "/dev/stdin") {
// To avoid problems with stdin and pipes, etc., we read the entire
// file in beforehand into a memory buffer, and then parcel it out
// from there.
std::ostringstream buffer;
while (std::cin.good() && ! std::cin.eof()) {
char line[8192];
std::cin.read(line, 8192);
std::streamsize count = std::cin.gcount();
buffer.write(line, count);
}
buffer.flush();
shared_ptr<std::istream> stream(new std::istringstream(buffer.str()));
parsing_context.push(stream);
} else {
parsing_context.push(pathname);
}
parsing_context.get_current().journal = journal.get();
parsing_context.get_current().master = acct;
try {
xact_count += journal->read(parsing_context);
}
catch (...) {
parsing_context.pop();
throw;
}
parsing_context.pop();
}
DEBUG("ledger.read", "xact_count [" << xact_count
<< "] == journal->xacts.size() [" << journal->xacts.size() << "]");
assert(xact_count == journal->xacts.size());
if (populated_data_files)
HANDLER(file_).data_files.clear();

View file

@ -85,7 +85,7 @@ static char* _strptime(const char *s, const char *format, struct tm *tm) {
if (tm->tm_wday == -1) return NULL;
s += len;
break;
// month name.
case 'b':
case 'B':

View file

@ -500,11 +500,7 @@ public:
void stabilize(const optional<date_t>& date = none);
bool is_valid() const {
#if BOOST_VERSION >= 105600
return start != NULL;
#else
return start;
#endif
return static_cast<bool>(start);
}
/** Find the current or next period containing date. Returns false if

View file

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

View file

@ -435,7 +435,7 @@ public:
return temp;
}
void in_place_roundto(int places);
value_t truncated() const {
value_t temp(*this);
temp.in_place_truncate();

View file

@ -235,7 +235,7 @@ public:
optional<string> note() const {
return ptr()->note;
}
bool has_tag(const string& tag) const {
return ptr()->has_tag(tag);
}

View file

@ -195,7 +195,7 @@ int mk_wcwidth(boost::uint32_t ucs)
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||

View file

@ -47,7 +47,9 @@ if (PYTHONINTERP_FOUND)
set_target_properties(check PROPERTIES DEPENDS ${_class}Test_${TestFile_Name})
endforeach()
list(APPEND CheckOptions CheckManpage CheckTexinfo CheckBaselineTests)
# CheckManpage and CheckTexinfo are disabled, since they do not work
# reliably yet, instead they are being run as a Travis CI report.
list(APPEND CheckOptions CheckBaselineTests) #CheckManpage CheckTexinfo
foreach(_class ${CheckOptions})
add_test(NAME ${_class}
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/${_class}.py

View file

@ -23,7 +23,6 @@ class CheckBaselineTests (CheckOptions):
'args-only',
'debug',
'download',
'file',
'force-color',
'force-pager',
'generated',
@ -33,6 +32,7 @@ class CheckBaselineTests (CheckOptions):
'no-pager',
'options',
'price-exp',
'revalued-total',
'seed',
'trace',
'verbose',

View file

@ -83,7 +83,7 @@ class CheckOptions (object):
self.missing_functions.add(function)
else:
functions.remove(function)
known_functions = ['tag', 'has_tag']
known_functions = ['tag', 'has_tag', 'meta', 'has_meta']
self.unknown_functions = {function for function in functions if function not in known_functions}
if len(self.missing_options):

View file

@ -108,6 +108,7 @@ class DocTests:
validate_command = False
try:
command = example[self.testin_token][self.testin_token]
command = re.sub(r'\\\n', '', command)
except KeyError:
if self.validate_dat_token in example:
command = '$ ledger bal'

View file

@ -17,7 +17,7 @@ end test
test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert3.dat -> 1
__ERROR__
While parsing file "$sourcepath/test/baseline/cmd-convert3.dat", line 1:
While parsing file "$sourcepath/test/baseline/cmd-convert3.dat", line 1:
While parsing CSV line:
01/01/2011,,
@ -26,7 +26,7 @@ end test
test -f /dev/null convert test/baseline/cmd-convert4.dat -> 1
__ERROR__
While parsing file "$sourcepath/test/baseline/cmd-convert4.dat", line 1:
While parsing file "$sourcepath/test/baseline/cmd-convert4.dat", line 1:
While parsing CSV line:
bogus,$10,

View file

@ -0,0 +1,20 @@
test cleared --file test/input/drewr3.dat --cleared-format "%-30(account) %15(get_at(total_expr, 0)) %15(get_at(total_expr, 1))\n%/"
Assets $ -3,804.00 $ 775.00
Assets:Checking $ 1,396.00 $ 775.00
Assets:Checking:Business $ 30.00 0
Assets:Savings $ -5,200.00 0
Equity:Opening Balances $ -1,000.00 $ -1,000.00
Expenses $ 6,654.00 $ 225.00
Expenses:Auto $ 5,500.00 0
Expenses:Books $ 20.00 0
Expenses:Escrow $ 300.00 0
Expenses:Food:Groceries $ 334.00 $ 225.00
Expenses:Interest:Mortgage $ 500.00 0
Income $ -2,030.00 0
Income:Salary $ -2,000.00 0
Income:Sales $ -30.00 0
Liabilities $ -63.60 0
Liabilities:MasterCard $ -20.00 0
Liabilities:Mortgage:Principal $ 200.00 0
Liabilities:Tithe $ -243.60 0
end test

View file

@ -2,16 +2,18 @@ account Assets:Cash
account Expenses:Phone
account Expenses:Rent
commodity GBP
tag bar
2012-03-20 Phone
2012-03-20 * Phone
; :bar:
Expenses:Phone 20.00 GBP
Assets:Cash
2012-03-21 Rent
2012-03-21 * Rent
Expenses:Rent 550.00 GBP
Assets:Cash
2012-03-22 Food
2012-03-22 * Food
; :food:
Expenses:Food 20.00 EUR
Assets:Cash
@ -27,8 +29,8 @@ test bal --explicit --strict
--------------------
0
__ERROR__
Warning: "$FILE", line 16: Unknown account 'Expenses:Food'
Warning: "$FILE", line 16: Unknown commodity 'EUR'
Warning: "$FILE", line 17: Unknown metadata tag 'food'
Warning: "$FILE", line 18: Unknown account 'Expenses:Food'
Warning: "$FILE", line 18: Unknown commodity 'EUR'
Warning: "$FILE", line 19: Unknown metadata tag 'food'
end test

View file

@ -0,0 +1,58 @@
2009/01/01 Sample 1a
Assets:Brokerage:Stocks 100 S
Assets:Brokerage:Cash -100 P
P 2009/01/01 00:00:00 S 2 P
2009/02/01 Sample 2a
Assets:Brokerage:Stocks 100 S @ 1 P
Assets:Brokerage:Cash
P 2009/02/01 00:00:00 S 4 P
2009/03/01 Sample 3a
Assets:Brokerage:Stocks 100 S @@ 100 P
Assets:Brokerage:Cash
P 2009/03/01 00:00:00 S 8 P
2009/04/01 Sample 4a
Assets:Brokerage:Cash 100 P
Assets:Brokerage:Stocks -100 S {1 P}
P 2009/04/01 00:00:00 S 16 P
; In this usage case, the top amount is always secondary
; 2010/01/01 Sample 1b
; Assets:Brokerage:Cash -100 P
; Assets:Brokerage:Stocks 100 S
;
; P 2010/01/01 00:00:00 S 2 P
2010/02/01 Sample 2b
Assets:Brokerage:Cash
Assets:Brokerage:Stocks 100 S @ 1 P
P 2010/02/01 00:00:00 S 4 P
2010/03/01 Sample 3b
Assets:Brokerage:Cash
Assets:Brokerage:Stocks 100 S @@ 100 P
P 2010/03/01 00:00:00 S 8 P
2010/04/01 Sample 4b
Assets:Brokerage:Stocks -100 S {1 P}
Assets:Brokerage:Cash 100 P
P 2010/04/01 00:00:00 S 16 P
test reg --market --no-revalued stocks
09-Jan-01 Sample 1a Asset:Brokerage:Stocks 200 P 200 P
09-Feb-01 Sample 2a Asset:Brokerage:Stocks 400 P 800 P
09-Mar-01 Sample 3a Asset:Brokerage:Stocks 800 P 2400 P
09-Apr-01 Sample 4a Asset:Brokerage:Stocks -1600 P 3200 P
10-Feb-01 Sample 2b Asset:Brokerage:Stocks 400 P 1200 P
10-Mar-01 Sample 3b Asset:Brokerage:Stocks 800 P 3200 P
10-Apr-01 Sample 4b Asset:Brokerage:Stocks -1600 P 4800 P
end test

View file

@ -0,0 +1,81 @@
2012-01-01 * Opening balance
Assets:Current 17.43 EUR
Assets:Investments 200 "LU02" @ 24.77 EUR
Assets:Investments 58 "LU02" @ 24.79900855 EUR
Equity:Opening balance
2012-01-01 * Opening balance
Assets:Pension 785.44 GBP
Assets:Pension 97.0017 "H2" @ 5.342999720204 GBP
Assets:Pension 4.3441 "H1" @ 5.289999915108 GBP
Equity:Opening balance
2012-01-01 * Opening balance: misc
Assets:Piggy bank 3.51 GBP
Equity:Opening balance
2012-01-01 * Opening balance
Assets:Rewards 9836 AAdvantage
Equity:Opening balance
2012-01-03 * Receivable
Assets:Current
Assets:Receivable -161.06 EUR
Assets:Receivable -9.99 GBP @@ 11.65 EUR
2012-01-27 * Test
Income:Test -2759.50 GBP
Income:Test -110.76 GBP
Assets:Foo 345.57 GBP
Expenses:Test 16.47 GBP
Expenses:Test 6.33 GBP
Expenses:Test 261.39 GBP
Assets:Current
test reg -X EUR -H --no-rounding
12-Jan-01 Opening balance Assets:Current 17.43 EUR 17.43 EUR
Assets:Investments 4959.80 EUR 4977.23 EUR
Assets:Investments 1438.34 EUR 6415.57 EUR
Equity:Opening balance -6409.77 EUR 5.80 EUR
12-Jan-01 Opening balance Assets:Pension 785.44 GBP 5.80 EUR
785.44 GBP
Assets:Pension 97.0017 H2 5.80 EUR
785.44 GBP
97.0017 H2
Assets:Pension 4.3441 H1 5.80 EUR
785.44 GBP
4.3441 H1
97.0017 H2
Equity:Opening balance -1326.70 GBP 5.80 EUR
-541.26 GBP
4.3441 H1
97.0017 H2
12-Jan-01 Opening balance: misc Assets:Piggy bank 3.51 GBP 5.80 EUR
-537.75 GBP
4.3441 H1
97.0017 H2
Equity:Opening balance -3.51 GBP 5.80 EUR
-541.26 GBP
4.3441 H1
97.0017 H2
12-Jan-01 Opening balance Assets:Rewards 9836 AAdvantage 9836 AAdvantage
5.80 EUR
-541.26 GBP
4.3441 H1
97.0017 H2
Equity:Opening balance -9836 AAdvantage 5.80 EUR
-541.26 GBP
4.3441 H1
97.0017 H2
12-Jan-03 Commodities revalued <Revalued> 0 5.80 EUR
12-Jan-03 Receivable Assets:Current 172.71 EUR 178.51 EUR
Assets:Receivable -161.06 EUR 17.45 EUR
Assets:Receivable -11.65 EUR 5.80 EUR
12-Jan-27 Test Income:Test -3218.04 EUR -3212.23 EUR
Income:Test -129.16 EUR -3341.40 EUR
Assets:Foo 402.99 EUR -2938.41 EUR
Expenses:Test 19.21 EUR -2919.20 EUR
Expenses:Test 7.38 EUR -2911.82 EUR
Expenses:Test 304.82 EUR -2606.99 EUR
Assets:Current 2612.80 EUR 5.80 EUR
end test

View file

@ -0,0 +1,48 @@
D 1000.00 EUR
D 1000.00 USD
D 1000.00 DM
2015-01-01 * Buy 2 FOO
Assets:Investments 2 FOO @@ 20.00 EUR
Assets:Cash -20.00 EUR
2015-05-01 * Spend on food
Expenses:Food 20.00 USD
; Just to be silly, always valuate *these* $20 as 30 DM, no matter what
; the user asks for with -V or -X
; VALUE:: 30 DM
Assets:Cash -20.00 USD
P 2015-05-01 USD 20 DM
P 2015-06-01 USD 22 DM
test bal assets:investments -V --value-expr "25.00 EUR"
50.00 EUR Assets:Investments
end test
test bal assets:investments -G --value-expr "date < [March 2015] ? 22.00 EUR : 25.00 EUR" --now "2015-02-20"
24.00 EUR Assets:Investments
end test
test bal assets:investments -G --value-expr "date < [March 2015] ? 22.00 EUR : 25.00 EUR" --now "2015-03-20"
30.00 EUR Assets:Investments
end test
test bal expenses:food
20.00 USD Expenses:Food
end test
test bal expenses:food -V
600.00 DM Expenses:Food
end test
test bal expenses:food -X "DM" --now "2015-05-02"
600.00 DM Expenses:Food
end test
test bal expenses:food -X "DM" --now "2015-06-02"
600.00 DM Expenses:Food
end test

11
test/regress/1057.test Normal file
View file

@ -0,0 +1,11 @@
2014/04/03 www.amazon.fr
Dépense:Loisir:Ordi:Matériel 101,50 € ; disque dur portable 2,5" 2000 Go
Dépense:Maison:Service:Poste
* Passif:Crédit:BanqueAccord -171,63 €
test -f test/regress/1057.test --now=2014/06/27 emacs
(("$sourcepath/test/regress/1057.test" 1 (21308 34912 0) nil "www.amazon.fr"
(2 "Dépense:Loisir:Ordi:Matériel" "101,50 €" nil " disque dur portable 2,5\" 2000 Go")
(3 "Dépense:Maison:Service:Poste" "70,13 €" nil)
(4 "Passif:Crédit:BanqueAccord" "-171,63 €" t)))
end test

View file

@ -0,0 +1,61 @@
test -f test/garbage-input.dat reg -> 29
__ERROR__
While parsing file "$sourcepath/test/garbage-input.dat", line 1:
Error: Directive '/*' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 32:
Error: Directive '/**' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 36:
Error: Directive '/**' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 66:
Error: No quantity specified for amount
While parsing file "$sourcepath/test/garbage-input.dat", line 69:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 78:
Error: Directive '};' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 82:
Error: Directive '/**' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 93:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 97:
Error: Directive '{' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 98:
Error: Directive 'public:' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 120:
Error: Directive 'protected:' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 131:
Error: Directive 'public:' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 711:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 740:
Error: Directive 'private:' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 749:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 750:
Error: Directive '};' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 752:
Error: Invalid date/time: line amount_t amoun
While parsing file "$sourcepath/test/garbage-input.dat", line 756:
Error: Directive '}' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 758:
Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/test/garbage-input.dat", line 762:
Error: Directive '}' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 764:
Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/test/garbage-input.dat", line 768:
Error: Directive '}' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 770:
Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/test/garbage-input.dat", line 774:
Error: Directive '}' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 776:
Error: Invalid date/time: line std::ostream&
While parsing file "$sourcepath/test/garbage-input.dat", line 782:
Error: Directive '}' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 783:
Error: Invalid date/time: line std::istream&
While parsing file "$sourcepath/test/garbage-input.dat", line 786:
Error: Directive '}' requires an argument
While parsing file "$sourcepath/test/garbage-input.dat", line 789:
Error: Unexpected whitespace at beginning of line
end test

View file

@ -1,43 +0,0 @@
test -f test/garbage-input.dat reg -> 20
__ERROR__
While parsing file "$sourcepath/test/garbage-input.dat", line 2:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 33:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 37:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 66:
Error: No quantity specified for amount
While parsing file "$sourcepath/test/garbage-input.dat", line 69:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 83:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 93:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 99:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 121:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 132:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 711:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 741:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 749:
Error: Unexpected whitespace at beginning of line
While parsing file "$sourcepath/test/garbage-input.dat", line 752:
Error: Invalid date/time: line amount_t amoun
While parsing file "$sourcepath/test/garbage-input.dat", line 758:
Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/test/garbage-input.dat", line 764:
Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/test/garbage-input.dat", line 770:
Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/test/garbage-input.dat", line 776:
Error: Invalid date/time: line std::ostream&
While parsing file "$sourcepath/test/garbage-input.dat", line 783:
Error: Invalid date/time: line std::istream&
While parsing file "$sourcepath/test/garbage-input.dat", line 789:
Error: Unexpected whitespace at beginning of line
end test

View file

@ -7,9 +7,16 @@ include_directories(${PROJECT_SOURCE_DIR}/src)
if (BUILD_LIBRARY)
add_executable(UtilTests t_times.cc)
if (CMAKE_SYSTEM_NAME STREQUAL Darwin AND HAVE_BOOST_PYTHON)
target_link_libraries(UtilTests ${PYTHON_LIBRARIES})
endif()
add_ledger_test(UtilTests)
add_executable(MathTests t_amount.cc t_commodity.cc t_balance.cc t_expr.cc)
add_executable(MathTests t_amount.cc t_commodity.cc t_balance.cc t_expr.cc t_value.cc)
set_source_files_properties(t_amount.cc PROPERTIES COMPILE_FLAGS "-Wno-unused-comparison")
if (CMAKE_SYSTEM_NAME STREQUAL Darwin AND HAVE_BOOST_PYTHON)
target_link_libraries(MathTests ${PYTHON_LIBRARIES})
endif()
add_ledger_test(MathTests)
set_target_properties(check PROPERTIES DEPENDS LedgerUtilTests)

714
test/unit/t_value.cc Normal file
View file

@ -0,0 +1,714 @@
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <system.hh>
#include "value.h"
using namespace ledger;
struct value_fixture {
value_fixture() {
times_initialize();
amount_t::initialize();
value_t::initialize();
// Cause the display precision for dollars to be initialized to 2.
amount_t x1("$1.00");
BOOST_CHECK(x1);
amount_t::stream_fullstrings = true; // make reports from UnitTests accurate
}
~value_fixture()
{
amount_t::stream_fullstrings = false;
amount_t::shutdown();
times_shutdown();
value_t::shutdown();
}
};
BOOST_FIXTURE_TEST_SUITE(value, value_fixture)
BOOST_AUTO_TEST_CASE(testConstructors)
{
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_t(NULL)));
value_t v4(date_t(parse_date("2014/08/14")));
value_t v5(2L);
value_t v6(4UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("3 EUR"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("tag"), true);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
}
BOOST_AUTO_TEST_CASE(testAssignment)
{
value_t::sequence_t s1;
value_t v1;
value_t v2 = true;
value_t v3 = boost::posix_time::from_time_t(time_t(NULL));
value_t v4 = date_t(parse_date("2014/08/14"));
value_t v5 = -2L;
value_t v6 = 4UL;
value_t v7 = 1.00;
value_t v8 = amount_t("4 GBP");
value_t v9 = balance_t("3 EUR");
value_t v10 = mask_t("regex");
value_t v11 = s1;
value_t v12 = value_t(string("$1"));
value_t v13 = value_t("2 CAD");
value_t v14 = value_t("comment", true);
value_t v15 = value_t(string("tag"), true);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
}
BOOST_AUTO_TEST_CASE(testEquality)
{
struct tm localtime;
strptime("10 February 2010", "%d %b %Y", &localtime);
time_t time_var = mktime(&localtime);
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_var));
value_t v4(date_t(parse_date("2014/08/14")));
value_t v5(2L);
value_t v6(2UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("4 GBP"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("comment"), true);
value_t v16;
BOOST_CHECK_EQUAL(v1, value_t());
BOOST_CHECK_EQUAL(v2, value_t(true));
BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::from_time_t(time_var)));
BOOST_CHECK(!(v4 == value_t(date_t(parse_date("2014/08/15")))));
value_t v19(amount_t("2"));
value_t v20(balance_t("2"));
BOOST_CHECK_EQUAL(v5, v6);
BOOST_CHECK_EQUAL(v5, v19);
BOOST_CHECK_EQUAL(v5, v20);
BOOST_CHECK(v19 == v5);
BOOST_CHECK(v19 == v20);
BOOST_CHECK(v19 == value_t(amount_t("2")));
BOOST_CHECK(v20 == v5);
BOOST_CHECK(v20 == v19);
BOOST_CHECK(v20 == value_t(balance_t(2L)));
BOOST_CHECK(v14 == v15);
BOOST_CHECK(v10 == value_t(mask_t("regex")));
BOOST_CHECK(v11 == value_t(s1));
BOOST_CHECK_THROW(v8 == v10, value_error);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
BOOST_CHECK(v19.valid());
BOOST_CHECK(v20.valid());
}
BOOST_AUTO_TEST_CASE(testSequence)
{
value_t::sequence_t s1;
value_t v1(s1);
BOOST_CHECK(v1.is_sequence());
v1.push_back(value_t(2L));
v1.push_back(value_t("3 GBP"));
value_t v2("3 GBP");
value_t seq(v1);
const value_t v3(seq);
value_t::sequence_t::iterator i = std::find(seq.begin(), seq.end(), v2);
if (i != seq.end())
BOOST_CHECK(v2 == *i);
value_t::sequence_t::const_iterator j = std::find(v3.begin(), v3.end(), v2);
if (j != v3.end())
BOOST_CHECK(v2 == *j);
BOOST_CHECK(v2 == seq[1]);
BOOST_CHECK(v2 == v3[1]);
v1.pop_back();
v1.pop_back();
v1.push_front(v2);
v1.push_front(value_t(2L));
BOOST_CHECK(v2 == v1[1]);
BOOST_CHECK(seq == v1);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(seq.valid());
}
BOOST_AUTO_TEST_CASE(testAddition)
{
struct tm localtime;
strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime);
time_t time_var = mktime(&localtime);
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_var));
value_t v4(date_t(parse_date("2014/08/14")));
value_t v5(2L);
value_t v6(2UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("4 GBP"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("comment"), true);
value_t v16(amount_t("2"));
v14 += v15;
BOOST_CHECK_EQUAL(v14, value_t(string("commentcomment"), true));
v14 += v12;
BOOST_CHECK_EQUAL(v14, value_t(string("commentcomment$1.00"), true));
v3 += value_t(2L);
strptime("10 February 2010 00:00:02", "%d %b %Y %H:%M:%S", &localtime);
BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::from_time_t(mktime(&localtime))));
v3 += value_t(amount_t("2"));
strptime("10 February 2010 00:00:04", "%d %b %Y %H:%M:%S", &localtime);
BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::from_time_t(mktime(&localtime))));
v4 += value_t(2L);
BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/16"))));
v4 += value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/18"))));
v5 += value_t(2L);
BOOST_CHECK_EQUAL(v5, value_t(4L));
v5 += value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v5, value_t(amount_t("6")));
v5 += v8;
v16 += value_t(2L);
v16 += value_t(amount_t("2"));
v16 += v8;
BOOST_CHECK_EQUAL(v5, v16);
v8 += value_t("6");
BOOST_CHECK_EQUAL(v8, v16);
value_t v17(6L);
v17 += value_t(amount_t("4 GBP"));
BOOST_CHECK_EQUAL(v8, v17);
value_t v18(6L);
v18 += v9;
value_t v19(amount_t("6"));
v19 += v9;
BOOST_CHECK_EQUAL(v18, v19);
v9 += value_t(2L);
v9 += value_t(amount_t("4"));
v9 += v19;
v18 += v19;
BOOST_CHECK_EQUAL(v9, v18);
value_t v20(s1);
v11 += value_t(2L);
v11 += value_t("4 GBP");
BOOST_CHECK_THROW(v11 += v20,value_error);
BOOST_CHECK_THROW(v10 += v8, value_error);
v20 += value_t(2L);
v20 += value_t("4 GBP");
BOOST_CHECK_EQUAL(v11, v20);
v11 += v20;
v20 += v20;
BOOST_CHECK_EQUAL(v11, v20);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
BOOST_CHECK(v16.valid());
BOOST_CHECK(v17.valid());
BOOST_CHECK(v18.valid());
BOOST_CHECK(v19.valid());
BOOST_CHECK(v20.valid());
}
BOOST_AUTO_TEST_CASE(testSubtraction)
{
struct tm localtime;
strptime("10 February 2010 00:00:04", "%d %b %Y %H:%M:%S", &localtime);
time_t time_var = mktime(&localtime);
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_var));
value_t v4(date_t(parse_date("2014/08/18")));
value_t v5(6L);
value_t v6(6UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("4 GBP"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("comment"), true);
value_t v16(amount_t("6"));
v3 -= value_t(2L);
strptime("10 February 2010 00:00:02", "%d %b %Y %H:%M:%S", &localtime);
BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::from_time_t(mktime(&localtime))));
v3 -= value_t(amount_t("2"));
strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime);
BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::from_time_t(mktime(&localtime))));
v4 -= value_t(2L);
BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/16"))));
v4 -= value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/14"))));
v5 -= value_t(2L);
BOOST_CHECK_EQUAL(v5, value_t(4L));
v5 -= value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v5, value_t(amount_t("2")));
v5 -= v8;
v16 -= value_t(2L);
v16 -= value_t(amount_t("2"));
v16 -= v8;
BOOST_CHECK_EQUAL(v5, v16);
v8 -= value_t("2");
BOOST_CHECK_EQUAL(-v8, v16);
value_t v18(6L);
v18 -= v9;
value_t v19(amount_t("6"));
v19 -= v9;
BOOST_CHECK_EQUAL(v18, v19);
v9 -= value_t(-2L);
v9 -= value_t(amount_t("-10"));
v9 -= value_t(amount_t("12 GBP"));
v9 -= v19;
BOOST_CHECK_EQUAL(v9, v18);
v18 -=v19;
BOOST_CHECK_EQUAL(v18, value_t("0"));
value_t v20(s1);
value_t v21(2L);
value_t v22("4 GBP");
v11.push_back(v21);
v11.push_back(v22);
BOOST_CHECK_THROW(v11 -= v20,value_error);
BOOST_CHECK_THROW(v10 -= v8, value_error);
v20.push_back(v21);
v20.push_back(v22);
v11 -= v20;
value_t v23(s1);
v23.push_back(value_t(0L));
v23.push_back(value_t("0"));
BOOST_CHECK_EQUAL(v11, v23);
v20 -= v21;
v20 -= v22;
BOOST_CHECK_EQUAL(v20, value_t(s1));
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
BOOST_CHECK(v16.valid());
BOOST_CHECK(v18.valid());
BOOST_CHECK(v19.valid());
BOOST_CHECK(v20.valid());
}
BOOST_AUTO_TEST_CASE(testMultiplication)
{
struct tm localtime;
strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime);
time_t time_var = mktime(&localtime);
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_var));
value_t v4(date_t(parse_date("2014/08/14")));
value_t v5(2L);
value_t v6(2UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("4 GBP"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("comment"), true);
value_t v16(amount_t("2"));
v14 *= value_t(2L);
BOOST_CHECK_EQUAL(v14, value_t(string("commentcomment"), true));
v5 *= value_t(2L);
BOOST_CHECK_EQUAL(v5, value_t(4L));
v5 *= value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v5, value_t(amount_t("8")));
v16 *= value_t(2L);
v16 *= value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v5, v16);
v8 *= v9;
BOOST_CHECK_EQUAL(v8, value_t("16 GBP"));
value_t v17(v9);
v9 *= value_t(2L);
BOOST_CHECK_EQUAL(v9, value_t("8 GBP"));
v17 += value_t(2L);
v17 *= value_t(2L);
value_t v18("8 GBP");
v18 += value_t(4L);
BOOST_CHECK_EQUAL(v17, v18);
value_t v20(s1);
v11.push_back(value_t(2L));
v11.push_back(value_t("2 GBP"));
v20.push_back(value_t(4L));
v20.push_back(value_t("4 GBP"));
v11 *= value_t(2L);
BOOST_CHECK_EQUAL(v11 ,v20);
BOOST_CHECK_THROW(v10 *= v8, value_error);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
BOOST_CHECK(v16.valid());
BOOST_CHECK(v17.valid());
BOOST_CHECK(v18.valid());
}
BOOST_AUTO_TEST_CASE(testDivision)
{
struct tm localtime;
strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime);
time_t time_var = mktime(&localtime);
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_var));
value_t v4(date_t(parse_date("2014/08/14")));
value_t v5(8L);
value_t v6(2UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("4 GBP"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("comment"), true);
value_t v16(amount_t("8"));
v5 /= value_t(2L);
BOOST_CHECK_EQUAL(v5, value_t(4L));
v5 /= value_t(amount_t("8"));
BOOST_CHECK_EQUAL(v5, value_t(amount_t("2")));
v16 /= value_t(2L);
v16 /= value_t(amount_t("2"));
BOOST_CHECK_EQUAL(v5, v16);
v8 /= v9;
v8 /= value_t(balance_t(2L));
BOOST_CHECK_EQUAL(v8, value_t("0.5 GBP"));
value_t v17(v9);
v9 /= value_t(2L);
BOOST_CHECK_EQUAL(v9, value_t("2 GBP"));
v17 /= value_t("2 GBP");
v17 /= value_t("2");
BOOST_CHECK_EQUAL(v17, value_t(balance_t("1 GBP")));
BOOST_CHECK_THROW(v10 /= v8, value_error);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
BOOST_CHECK(v16.valid());
BOOST_CHECK(v17.valid());
}
BOOST_AUTO_TEST_CASE(testType)
{
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_t(NULL)));
value_t v4(date_t(parse_date("2014/08/14")));
value_t v5(2L);
value_t v6(4UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("3 EUR"));
value_t v10(mask_t("regex"));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string("tag"), true);
BOOST_CHECK(v1.is_null());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK(v3.is_datetime());
BOOST_CHECK(v4.is_date());
BOOST_CHECK(v5.is_long());
BOOST_CHECK(v6.is_amount());
BOOST_CHECK(v7.is_amount());
BOOST_CHECK(v8.is_amount());
BOOST_CHECK(v9.is_balance());
BOOST_CHECK(v10.is_mask());
BOOST_CHECK(v11.is_sequence());
BOOST_CHECK(v12.is_amount());
BOOST_CHECK(v13.is_amount());
BOOST_CHECK(v14.is_string());
BOOST_CHECK(v15.is_string());
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
}
BOOST_AUTO_TEST_CASE(testForZero)
{
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_t(NULL)));
value_t v4(date_t(0));
value_t v5(2L);
value_t v6(0UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("0"));
value_t v10(mask_t(""));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("2 CAD");
value_t v14("comment", true);
value_t v15(string(""), true);
BOOST_CHECK(v1.is_null());
BOOST_CHECK(v2.is_nonzero());
BOOST_CHECK(!v3.is_zero());
BOOST_CHECK(v4.is_nonzero());
BOOST_CHECK(v5.is_nonzero());
BOOST_CHECK(v6.is_realzero());
BOOST_CHECK(v7.is_nonzero());
BOOST_CHECK(v8.is_nonzero());
BOOST_CHECK(v9.is_zero());
BOOST_CHECK_THROW(v10.is_zero(), value_error);
BOOST_CHECK(v11.is_zero());
BOOST_CHECK(v12.is_nonzero());
BOOST_CHECK(v13.is_nonzero());
BOOST_CHECK(v14.is_nonzero());
BOOST_CHECK(v15.is_zero());
v11.push_back(v6);
BOOST_CHECK(v11.is_nonzero());
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
}
BOOST_AUTO_TEST_CASE(testNegation)
{
value_t::sequence_t s1;
value_t v1;
value_t v2(true);
value_t v3(boost::posix_time::from_time_t(time_t(NULL)));
value_t v4(date_t(parse_date("2014/08/09")));
value_t v5(2L);
value_t v6(0UL);
value_t v7(1.00);
value_t v8(amount_t("4 GBP"));
value_t v9(balance_t("4 GBP"));
value_t v10(mask_t(""));
value_t v11(s1);
value_t v12(string("$1"));
value_t v13("$-1");
value_t v14("comment", true);
value_t v15(string("comment"), true);
BOOST_CHECK_THROW(v1.negated(), value_error);
BOOST_CHECK_EQUAL(v2.negated(), value_t(false));
v5.in_place_negate();
BOOST_CHECK_EQUAL(v5, value_t(-2L));
v8.in_place_negate();
v9.in_place_negate();
BOOST_CHECK_EQUAL(v8, v9);
BOOST_CHECK_THROW(v10.negated(), value_error);
BOOST_CHECK_EQUAL(-v12, v13);
BOOST_CHECK_THROW(-v14, value_error);
BOOST_CHECK(v1.valid());
BOOST_CHECK(v2.valid());
BOOST_CHECK(v3.valid());
BOOST_CHECK(v4.valid());
BOOST_CHECK(v5.valid());
BOOST_CHECK(v6.valid());
BOOST_CHECK(v7.valid());
BOOST_CHECK(v8.valid());
BOOST_CHECK(v9.valid());
BOOST_CHECK(v10.valid());
BOOST_CHECK(v11.valid());
BOOST_CHECK(v12.valid());
BOOST_CHECK(v13.valid());
BOOST_CHECK(v14.valid());
BOOST_CHECK(v15.valid());
}
BOOST_AUTO_TEST_SUITE_END()

16
tools/travis-before_install.sh Executable file
View file

@ -0,0 +1,16 @@
#!/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

22
tools/travis-install.sh Executable file
View file

@ -0,0 +1,22 @@
#!/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