[osmium-tool] 01/07: Imported Upstream version 1.3.1

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Wed Jun 8 19:47:29 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository osmium-tool.

commit 0ed0bdac1c100d1e1182b416576d13e74830517b
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Wed Jun 8 20:43:41 2016 +0200

    Imported Upstream version 1.3.1
---
 .travis.yml                                        |  149 +-
 CHANGELOG.md                                       |   26 +-
 CMakeLists.txt                                     |   16 +-
 Makefile                                           |    5 +-
 README.md                                          |    4 +-
 cmake/run_test_compare_output.cmake                |   12 +-
 man/common-options.md                              |    9 +
 man/input-options.md                               |    8 +
 man/osmium-add-locations-to-ways.md                |   94 +
 man/osmium-apply-changes.md                        |   48 +-
 man/osmium-cat.md                                  |   46 +-
 man/osmium-changeset-filter.md                     |   66 +-
 man/osmium-check-refs.md                           |   36 +-
 man/osmium-fileinfo.md                             |   17 +-
 man/osmium-getid.md                                |  110 +-
 man/osmium-merge-changes.md                        |   58 +-
 man/osmium-renumber.md                             |   69 +-
 man/osmium-show.md                                 |   95 +
 man/osmium-sort.md                                 |   52 +-
 man/osmium-time-filter.md                          |   50 +-
 man/osmium.md                                      |   37 +
 man/output-options.md                              |   26 +
 scripts/travis_script.sh                           |   36 -
 src/CMakeLists.txt                                 |    2 +-
 src/cmd.cpp                                        |   84 +
 src/cmd.hpp                                        |   40 +-
 src/cmd_factory.cpp                                |    2 +-
 src/command_add_locations_to_ways.cpp              |  173 ++
 src/command_add_locations_to_ways.hpp              |   76 +
 src/command_apply_changes.cpp                      |   26 +-
 src/command_apply_changes.hpp                      |   10 +-
 src/command_cat.cpp                                |   55 +-
 src/command_cat.hpp                                |   12 +-
 src/command_changeset_filter.cpp                   |   26 +-
 src/command_changeset_filter.hpp                   |   10 +-
 src/command_check_refs.cpp                         |  100 +-
 src/command_check_refs.hpp                         |   10 +-
 src/command_fileinfo.cpp                           |   24 +-
 src/command_fileinfo.hpp                           |   10 +-
 src/command_getid.cpp                              |  369 ++-
 src/command_getid.hpp                              |   52 +-
 src/command_help.cpp                               |    9 +-
 src/command_help.hpp                               |   11 +-
 src/command_merge_changes.cpp                      |   26 +-
 src/command_merge_changes.hpp                      |   10 +-
 src/command_renumber.cpp                           |  214 +-
 src/command_renumber.hpp                           |   96 +-
 src/command_show.cpp                               |  225 ++
 src/{command_cat.hpp => command_show.hpp}          |   30 +-
 src/command_sort.cpp                               |   26 +-
 src/command_sort.hpp                               |   10 +-
 src/command_time_filter.cpp                        |   24 +-
 src/command_time_filter.hpp                        |   11 +-
 src/exception.hpp                                  |    2 +-
 src/io.cpp                                         |   31 +-
 src/main.cpp                                       |   13 +-
 test/CMakeLists.txt                                |   27 +-
 test/add-locations-to-ways/CMakeLists.txt          |   17 +
 .../input.osm}                                     |   11 +-
 .../output-n.osm}                                  |   19 +-
 test/add-locations-to-ways/output.osm              |   17 +
 test/cat/output1.osm.opl                           |    6 +-
 test/changeset-filter/output1-all.osm              |    2 +-
 test/changeset-filter/output1-first.osm            |    2 +-
 test/check-refs/CMakeLists.txt                     |   44 +-
 test/check-refs/fail-n-in-r.osm                    |    6 +
 test/check-refs/{nw-okay.osm => fail-n-in-w.osm}   |    6 -
 test/check-refs/fail-r-in-r-1.osm                  |    6 +
 test/check-refs/fail-r-in-r-2.osm                  |    6 +
 test/check-refs/fail-w-in-r.osm                    |    6 +
 test/check-refs/okay-r-in-r.osm                    |    9 +
 test/check-refs/{nw-okay.osm => okay.osm}          |    4 +
 test/check-refs/{nw-okay.osm => way-okay.osm}      |    2 +-
 test/formats/f1.osm.opl                            |    8 +-
 test/getid/CMakeLists.txt                          |   38 +
 test/getid/idfile                                  |    5 +
 test/getid/in10.id                                 |    1 +
 test/getid/in10.osm                                |    4 +
 test/getid/in19.id                                 |    1 +
 test/getid/in19.osm                                |    4 +
 test/getid/in21.id                                 |    1 +
 test/getid/in21.osm                                |    7 +
 test/getid/in29.id                                 |    1 +
 test/getid/in29.osm                                |    7 +
 test/getid/in30.id                                 |    1 +
 test/getid/in30.osm                                |    8 +
 test/getid/in31.id                                 |    1 +
 test/getid/in31.osm                                |    6 +
 test/getid/in32.id                                 |    1 +
 test/getid/in32.osm                                |    6 +
 test/getid/in39.id                                 |    1 +
 test/getid/in39.osm                                |    6 +
 test/getid/out10.osm                               |    4 +
 test/getid/out21.osm                               |    9 +
 test/{check-refs/nw-okay.osm => getid/out30.osm}   |    6 +-
 test/{sort/output-bounds.osm => getid/out31.osm}   |   15 +-
 test/getid/out32.osm                               |    7 +
 test/getid/relloop-out.osm                         |    9 +
 test/getid/relloop.id                              |    2 +
 test/getid/relloop.osm                             |    9 +
 .../output-bounds.osm => getid/source-no-rr.osm}   |   18 +-
 test/{sort/output-bounds.osm => getid/source.osm}  |   21 +-
 test/help/CMakeLists.txt                           |    8 +-
 test/include/catch.hpp                             | 3045 ++++++++++++--------
 test/misc/CMakeLists.txt                           |    2 +-
 .../nw-okay.osm => order/fail-order-n.osm}         |   11 +-
 .../nw-okay.osm => order/fail-order-r.osm}         |    7 +
 .../nwr-okay.osm => order/fail-order-rw.osm}       |    5 +-
 .../nw-okay.osm => order/fail-order-w.osm}         |    9 +-
 .../nw-okay.osm => order/fail-order-wn.osm}        |    9 +-
 test/renumber/CMakeLists.txt                       |   24 +-
 test/sort/output-bounds.osm                        |    2 +-
 zsh_completion/_osmium                             |   53 +-
 113 files changed, 4453 insertions(+), 2007 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 6c57616..62969a7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,9 +23,13 @@ matrix:
           compiler: gcc
           env: BUILD_TYPE=Release
         - os: osx
+          # https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
+          osx_image: xcode7.3 # upgrades clang from 6 -> 7
           compiler: clang
           env: BUILD_TYPE=Dev
         - os: osx
+          # https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
+          osx_image: xcode7.3 # upgrades clang from 6 -> 7
           compiler: clang
           env: BUILD_TYPE=Release
 
@@ -42,9 +46,150 @@ addons:
             - libboost-program-options1.55-dev
             - pandoc
 
+##############################
+
+matrix:
+  include:
+
+    # 1/ Linux Clang Builds
+    - os: linux
+      compiler: clang
+      addons:
+        apt:
+          sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['clang-3.5', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='clang++-3.5' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons:
+        apt:
+          sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['clang-3.5', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='clang++-3.5' BUILD_TYPE='Dev'
+
+
+    - os: linux
+      compiler: clang
+      addons:
+        apt:
+          sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['clang-3.6', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='clang++-3.6' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons:
+        apt:
+          sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['clang-3.6', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='clang++-3.6' BUILD_TYPE='Dev'
+
+
+    - os: linux
+      compiler: clang
+      addons:
+        apt:
+          sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['clang-3.7', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='clang++-3.7' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons:
+        apt:
+          sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['clang-3.7', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='clang++-3.7' BUILD_TYPE='Dev'
+
+
+    # 2/ Linux GCC Builds
+    - os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['g++-4.8', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['g++-4.8', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Dev'
+
+
+    - os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['g++-4.9', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='g++-4.9' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['g++-4.9', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='g++-4.9' BUILD_TYPE='Dev'
+
+
+    - os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['g++-5', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='g++-5' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test', 'boost-latest']
+          packages: ['g++-5', 'libboost1.55-all-dev', 'pandoc']
+      env: COMPILER='g++-5' BUILD_TYPE='Dev'
+
+
+    # 3/ OSX Clang Builds
+    - os: osx
+      osx_image: xcode6.4
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Dev'
+
+    - os: osx
+      osx_image: xcode6.4
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Release'
+
+
+    - os: osx
+      osx_image: xcode7
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Dev'
+
+    - os: osx
+      osx_image: xcode7
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Release'
+
+
 install:
-    - git clone --quiet --depth 1 https://github.com/osmcode/libosmium.git ../libosmium
+  - git clone --quiet --depth 1 https://github.com/osmcode/libosmium.git ../libosmium
+  - |
+    if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+      brew install cmake boost
+    fi
+
+before_script:
+  - cd ${TRAVIS_BUILD_DIR}
+  - mkdir build && cd build
+  - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE}
 
 script:
-    - scripts/travis_script.sh
+  - make VERBOSE=1 && ctest --output-on-failure
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e4c774..03d4277 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,27 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
+## [1.3.1] - 2016-06-08
+
+### Added
+
+- Check that input files are correctly ordered in the `renumber` and
+  `check-refs` commands.
+- Most commands now show the memory used in verbose mode.
+- New option `-h`/`--help` on most commands shows command line options.
+- New `--default-type` option to getid command.
+- The `getid` command can now recursively add all referenced objects.
+- New `show` command to quickly see OSM file contents.
+- New `add-locations-to-ways` command.
+
+### Changed
+
+- Much faster and more memory efficient implementation of the `renumber`
+  command.
+- The `getid` command can now read IDs from *stdin*.
+- Also show libosmium version when running `version` subcommand.
+
+
 ## [1.3.0] - 2015-11-17
 
 ### Added
@@ -58,7 +79,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 ### Fixed
 
-- Remove license thats not applicable from LICENSE-rapidjson.txt.
+- Remove license that's not applicable from LICENSE-rapidjson.txt.
 - Renumbering didn't work properly in some cases.
 - Fix error checking in renumber command.
 
@@ -105,7 +126,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Minor updates to documentation and build system
 
 
-[unreleased]: https://github.com/osmcode/osmium-tool/compare/v1.3.0...HEAD
+[unreleased]: https://github.com/osmcode/osmium-tool/compare/v1.3.1...HEAD
+[1.3.1]: https://github.com/osmcode/osmium-tool/compare/v1.3.0...v1.3.1
 [1.3.0]: https://github.com/osmcode/osmium-tool/compare/v1.2.1...v1.3.0
 [1.2.1]: https://github.com/osmcode/osmium-tool/compare/v1.2.0...v1.2.1
 [1.2.0]: https://github.com/osmcode/osmium-tool/compare/v1.1.0...v1.2.0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9a05968..4bbd470 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,7 @@ project(osmium)
 
 set(OSMIUM_VERSION_MAJOR 1)
 set(OSMIUM_VERSION_MINOR 3)
-set(OSMIUM_VERSION_PATCH 0)
+set(OSMIUM_VERSION_PATCH 1)
 
 set(OSMIUM_VERSION ${OSMIUM_VERSION_MAJOR}.${OSMIUM_VERSION_MINOR}.${OSMIUM_VERSION_PATCH})
 
@@ -83,7 +83,11 @@ function(add_man_page _section _name)
     file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/man/man${_section})
     set(_output_file ${CMAKE_BINARY_DIR}/man/man${_section}/${_name}.${_section})
     set(_source_file ${CMAKE_SOURCE_DIR}/man/${_name}.md)
-    set(_install_dir "share/man/man{$_section}")
+    set(_dest_file ${CMAKE_BINARY_DIR}/man-source/${_name}.md)
+    file(READ ${CMAKE_SOURCE_DIR}/man/common-options.md MAN_COMMON_OPTIONS)
+    file(READ ${CMAKE_SOURCE_DIR}/man/input-options.md MAN_INPUT_OPTIONS)
+    file(READ ${CMAKE_SOURCE_DIR}/man/output-options.md MAN_OUTPUT_OPTIONS)
+    configure_file(${_source_file} ${_dest_file} @ONLY)
     string(TOUPPER ${_name} _name_upcase)
     add_custom_command(OUTPUT ${_output_file}
         COMMAND ${PANDOC}
@@ -91,8 +95,8 @@ function(add_man_page _section _name)
             --variable "title=${_name_upcase}"
             --variable "section=${_section}"
             -o ${_output_file}
-            ${_source_file}
-        DEPENDS ${_source_file} man/manpage.template
+            ${_dest_file}
+        DEPENDS ${_source_file} man/manpage.template man/common-options.md man/input-options.md man/output-options.md
         WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
         COMMENT "Building manpage ${_name}.${_section}"
         VERBATIM)
@@ -116,11 +120,13 @@ if(PANDOC)
     add_man_page(1 osmium)
     add_man_page(1 osmium-apply-changes)
     add_man_page(1 osmium-cat)
+    add_man_page(1 osmium-changeset-filter)
     add_man_page(1 osmium-check-refs)
     add_man_page(1 osmium-fileinfo)
     add_man_page(1 osmium-getid)
     add_man_page(1 osmium-merge-changes)
     add_man_page(1 osmium-renumber)
+    add_man_page(1 osmium-show)
     add_man_page(1 osmium-sort)
     add_man_page(1 osmium-time-filter)
     add_man_page(5 osmium-file-formats)
@@ -193,7 +199,7 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}"
 # In 'Dev' mode: compile with very strict warnings and turn them into errors.
 if(CMAKE_BUILD_TYPE STREQUAL "Dev")
     if(NOT MSVC)
-        add_definitions(-Werror)
+        add_definitions(-Werror -Wno-unused-variable)
     endif()
     add_definitions(${OSMIUM_WARNING_OPTIONS})
 endif()
diff --git a/Makefile b/Makefile
index 056945c..80d0337 100644
--- a/Makefile
+++ b/Makefile
@@ -11,5 +11,8 @@ distclean:
 deb:
 	debuild -I -us -uc
 
-.PHONY: clean distclean deb
+test:
+	cd build && ctest
+
+.PHONY: clean distclean deb test
 
diff --git a/README.md b/README.md
index 7128bfd..7097574 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,7 @@ will be built, if not they will not be built.
 
 ## Tests
 
-Call `ctest` to run the tests after build.
+Call `ctest` in the build directory to run the tests after build.
 
 More extensive tests of the libosmium I/O system can also be run. See
 `test/io/Makefile.in` for instructions.
@@ -78,7 +78,7 @@ More extensive tests of the libosmium I/O system can also be run. See
 
 ## License
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is available under the GNU GENERAL PUBLIC LICENSE Version 3.
 See the file LICENSE.txt for the complete text of the license.
diff --git a/cmake/run_test_compare_output.cmake b/cmake/run_test_compare_output.cmake
index 1b60d6a..40b1e9b 100644
--- a/cmake/run_test_compare_output.cmake
+++ b/cmake/run_test_compare_output.cmake
@@ -1,7 +1,12 @@
 #
-#  Runs a test command given in the variable 'cmd' in directory 'dir'.
+#  First, if variable 'tmpdir' ist set, this directory will be removed with
+#  all its content and recreated.
+#
+#  Then runs a test command given in the variable 'cmd' in directory 'dir'.
 #  Checks that the return code is 0.
 #  Checks that there is nothing on stderr.
+#  If the variable 'cmd2' is set, the command will be run and checked in the
+#  same manner.
 #  Compares output on stdout with reference file in variable 'reference'.
 #
 
@@ -21,6 +26,11 @@ if(NOT output)
     message(FATAL_ERROR "Variable 'output' not defined")
 endif()
 
+if(tmpdir)
+    file(REMOVE_RECURSE ${tmpdir})
+    file(MAKE_DIRECTORY ${tmpdir})
+endif()
+
 message("Executing: ${cmd}")
 separate_arguments(cmd)
 
diff --git a/man/common-options.md b/man/common-options.md
new file mode 100644
index 0000000..eac2bf6
--- /dev/null
+++ b/man/common-options.md
@@ -0,0 +1,9 @@
+
+# COMMON OPTIONS
+
+-h, --help
+:   Show usage help.
+
+-v, --verbose
+:   Set verbose mode. The program will output information about what it is
+    doing to *stderr*.
diff --git a/man/input-options.md b/man/input-options.md
new file mode 100644
index 0000000..c4e27cd
--- /dev/null
+++ b/man/input-options.md
@@ -0,0 +1,8 @@
+
+# INPUT OPTIONS
+
+-F, --input-format=FORMAT
+:   The format of the input file(s). Can be used to set the input format if it
+    can't be autodetected from the file name(s). This will set the format for
+    all input files, there is no way to set the format for some input files
+    only. See **osmium-file-formats**(5) or the libosmium manual for details.
diff --git a/man/osmium-add-locations-to-ways.md b/man/osmium-add-locations-to-ways.md
new file mode 100644
index 0000000..19414dc
--- /dev/null
+++ b/man/osmium-add-locations-to-ways.md
@@ -0,0 +1,94 @@
+
+# NAME
+
+osmium-add-locations-to-ways - add node locations to ways in OSM file
+
+
+# SYNOPSIS
+
+**osmium add-locations-to-ways** \[*OPTIONS*\] *OSM-FILE*...
+
+
+# DESCRIPTION
+
+Usually only nodes have locations and the ways refer to those locations via the
+IDs of the nodes. This program will copy the input file(s) to the output,
+taking the locations from the nodes and adding them to the ways. This makes
+it easier for other programs to assemble the way geometries.
+
+Nodes without any tags will not be copied (unless the **--keep-untagged-nodes**,
+**-n** option is used). The size of the output file will be similar or a bit
+smaller than the input file (unless the **--keep-untagged-nodes**,
+**-n** option is used in which case it will be a lot bigger).
+
+Note that the OSM files generated by this command use a non-standard format
+extension.
+
+The node locations have to be kept in memory while doings this. Use the
+**--index-type**, **-i** option to set the index type used. Default is
+`sparse_mmap_array` on Linux and `sparse_mem_array` on OSX/Windows. This is
+the right index type for small to medium sized extracts. For large
+(continent-sized) extracts or the full planet use `dense_mmap_array` on Linux
+or `dense_mem_array` on OSX/Windows.
+
+This program will not work on full history files.
+
+
+# OPTIONS
+
+-i, --index-type=TYPE
+:   Set the index type.
+
+-I, --show-index-types
+:   Shows a list of available index types. It depends on your operating system
+    which index types are available.
+
+-n, --keep-untagged-nodes
+:   Keep the untagged nodes in the output file.
+
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
+
+# DIAGNOSTICS
+
+**osmium add-locations-to-ways** exits with exit code
+
+0
+  ~ if everything went alright,
+
+1
+  ~ if there was an error processing the data, or
+
+2
+  ~ if there was a problem with the command line arguments.
+
+
+# MEMORY USAGE
+
+**osmium add-locations-to-ways** needs to keep all node locations in memory.
+It depends on the index type used how much memory is needed:
+
+* For `sparse` types 16 bytes per node in the input file are used.
+* For `dense` types 8 bytes times the largest node ID in the input file
+  are used.
+
+The `*_mem_*` types use potentially up to twice this amount.
+
+
+# EXAMPLES
+
+Add node locations to an extract keeping all nodes:
+
+    osmium add-locations-to-ways -n -o germany-low.osm.pbf germany.osm.pbf
+
+Add node locations to a planet file (without untagged nodes):
+
+    osmium add-locations-to-ways -i dense_mmap_array -o planet-low.osm.pbf planet.osm.pbf
+
+
+# SEE ALSO
+
+* **osmium**(1), **osmium-file-formats**(5)
+* [Osmium website](http://osmcode.org/osmium)
+
diff --git a/man/osmium-apply-changes.md b/man/osmium-apply-changes.md
index 90db8c2..2d9c38e 100644
--- a/man/osmium-apply-changes.md
+++ b/man/osmium-apply-changes.md
@@ -18,43 +18,9 @@ Objects in change files will be sorted by type, ID, and version, so it doesn't
 matter in what order the change files are given or in what order they contain
 the data.
 
-**osmium apply-changes** keeps the contents of the change files in main memory,
-so the data has to fit in there!
-
 
 # OPTIONS
 
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    See **osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input files. Can be used to set the input format if it
-    can't be autodetected from the file names. This will set the format for
-    all input files, there is no way to set the format for some input files
-    only. See **osmium-file-formats**(5) or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
-
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
-
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
-
 -r, --remove-deleted
 :   Remove deleted objects from the output. If this is not set, deleted objects
     will be in the output with the visible flag set to false.
@@ -62,10 +28,9 @@ so the data has to fit in there!
 -s, --simplify
 :   Only write the last version of any object to the output.
 
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -81,6 +46,13 @@ so the data has to fit in there!
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium apply-changes** keeps the contents of all the change files in main
+memory. This will take roughly 10 times as much memory as the files take on
+disk in *.osm.bz2* format.
+
+
 # EXAMPLES
 
 Apply changes in `362.osc.gz` to planet file and write result to `new.osm.pbf`:
diff --git a/man/osmium-cat.md b/man/osmium-cat.md
index 7056a1a..d35703a 100644
--- a/man/osmium-cat.md
+++ b/man/osmium-cat.md
@@ -6,7 +6,7 @@ osmium-cat - concatenate OSM files and convert to different formats
 
 # SYNOPSIS
 
-**osmium cat** \[*OPTIONS*\] *INPUT-FILE*...
+**osmium cat** \[*OPTIONS*\] *OSM-FILE*...
 
 
 # DESCRIPTION
@@ -20,45 +20,13 @@ can be used to convert OSM files from one format into another.
 
 # OPTIONS
 
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    **See osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input files. Can be used to set the input format if it
-    can't be autodetected from the file names. This will set the format for
-    all input files, there is no way to set the format for some input files
-    only. See **osmium-file-formats**(5) or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
-
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
-
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
-
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
-
 -t, --object-type=TYPE
 :   Read only objects of given type (*node*, *way*, *relation*, *changeset*).
     By default all types are read. This option can be given multiple times.
 
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -74,6 +42,12 @@ can be used to convert OSM files from one format into another.
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium cat** does all its work on the fly and doesn't keep much data in
+main memory.
+
+
 # EXAMPLES
 
 Convert a PBF file to a compressed XML file:
diff --git a/man/osmium-changeset-filter.md b/man/osmium-changeset-filter.md
index 1ac1be3..b63f9b9 100644
--- a/man/osmium-changeset-filter.md
+++ b/man/osmium-changeset-filter.md
@@ -6,7 +6,7 @@ osmium-changeset-filter - filter changesets from OSM changeset file
 
 # SYNOPSIS
 
-**osmium changeset-filter** \[*OPTIONS*\] *INPUT-FILE*
+**osmium changeset-filter** \[*OPTIONS*\] *OSM-CHANGESET-FILE*
 
 
 # DESCRIPTION
@@ -14,42 +14,20 @@ osmium-changeset-filter - filter changesets from OSM changeset file
 Copy the changesets matching all the given criteria to the output. Matching
 criteria are given through command line options.
 
+# FILTER OPTIONS
 
-# OPTIONS
-
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    **See osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input file. Can be used to set the input format if it
-    can't be autodetected from the file names. See **osmium-file-formats**(5)
-    or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
-
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
+-a, --after=TIMESTAMP
+:   Only copy changesets closed after the given time.
+    This will always include all open changesets.
 
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
+-b, --before=TIMESTAMP
+:   Only copy changesets created before the given time.
 
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
+-c, --with-changes
+:   Only copy changesets with changes.
 
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
+-C, --without-changes
+:   Only copy changesets without changes.
 
 -d, --with-discussion
 :   Only copy changesets with discussions, ie changesets with at least one
@@ -59,12 +37,6 @@ criteria are given through command line options.
 :   Only copy changesets without discussions, ie changesets without any
     comments.
 
--c, --with-changes
-:   Only copy changesets with changes.
-
--C, --without-changes
-:   Only copy changesets without changes.
-
 --open
 :   Only copy open changesets.
 
@@ -77,13 +49,9 @@ criteria are given through command line options.
 -U, --uid=UID
 :   Only copy changesets by the given user ID.
 
--a, --after=TIMESTAMP
-:   Only copy changesets closed after the given time.
-    This will always include all open changesets.
-
--b, --before=TIMESTAMP
-:   Only copy changesets created before the given time.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -99,6 +67,12 @@ criteria are given through command line options.
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium changeset-filter** does all its work on the fly and doesn't keep much
+data in main memory.
+
+
 # EXAMPLES
 
 To see all changesets by user "foo":
diff --git a/man/osmium-check-refs.md b/man/osmium-check-refs.md
index cf9390d..09584d4 100644
--- a/man/osmium-check-refs.md
+++ b/man/osmium-check-refs.md
@@ -6,49 +6,51 @@ osmium-check-refs - check referential integrity of OSM file
 
 # SYNOPSIS
 
-**osmium check-refs** \[*OPTIONS*\] *INPUT-FILE*
+**osmium check-refs** \[*OPTIONS*\] *OSM-DATA-FILE*
 
 
 # DESCRIPTION
 
 Ways in OSM files refer to OSM nodes; relations refer to nodes, ways, or other
-relations. This command checks whether all objects referenced in the input
-file are also present in the input file.
+relations. This command checks whether all objects *referenced* in the input
+file are also *present* in the input file.
 
 Referential integrity is often broken in extracts. This can lead to problems
 with some uses of the OSM data. Use this command to make sure your data is
 good.
 
-This command will do the check in one pass through the input data. It needs
-enough main memory to store all temporary data. Largest memory need will be
-1 bit for each node ID, thats roughly 500 MB these days (Summer 2015).
-
 If the option -r is not given, this command will only check if all nodes
-references in ways are in the file, with the option, relations will also be
+referenced in ways are in the file, with the option, relations will also be
 checked.
 
 This command expects the input file to be ordered in the usual way: First
 nodes in order of ID, then ways in order of ID, then relations in order of ID.
 
+This command will only work for OSM data files, not OSM history files or
+change files.
 
-# OPTIONS
 
--F, --input-format=FORMAT
-:   The format of the input file. Can be used to set the input format if it
-    can't be autodetected from the file name. See **osmium-file-formats**(5)
-    or the libosmium manual for details.
+# OPTIONS
 
 -i, --show-ids
-:   Print all missing IDs to stdout. If you don't give this option, only a
+:   Print all missing IDs to *stdout*. If you don't give this option, only a
     summary is shown.
 
 -r, --check-relations
 :   Also check referential integrity of relations. Without this option, only
     nodes in ways are checked.
 
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+
+# MEMORY USAGE
+
+**osmium check-refs** will do the check in one pass through the input data. It
+needs enough main memory to store all temporary data.
+
+Largest memory need will be about 1 bit for each node ID, for a full planet
+that's roughly 500 MB these days (Summer 2015). With the **-r**,
+**--check-relations** option memory use will be a bit bigger.
 
 
 # DIAGNOSTICS
diff --git a/man/osmium-fileinfo.md b/man/osmium-fileinfo.md
index 05fc3f1..5c5f507 100644
--- a/man/osmium-fileinfo.md
+++ b/man/osmium-fileinfo.md
@@ -54,11 +54,6 @@ Data
 :   Read the complete file and show additional information. The default
     is to read only the header of the file.
 
--F, --input-format=FORMAT
-:   The format of the input file. Can be used to set the input file format
-    if it can't be autodetected from the file name.
-    See **osmium-file-formats**(5) or the libosmium manual for details.
-
 -g, --get=VARIABLE
 :   Get value of VARIABLE. Can not be used together with --json.
 
@@ -68,10 +63,8 @@ Data
 -j, --json
 :   Output in JSON format. Can not be used together with --get.
 
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
 
 # VARIABLES
 
@@ -123,6 +116,12 @@ are in the format `(xmin, ymin, xmax, ymax)`.
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium fileinfo** does all its work on the fly and doesn't keep much data in
+main memory.
+
+
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
diff --git a/man/osmium-getid.md b/man/osmium-getid.md
index b25d56d..40f6c5d 100644
--- a/man/osmium-getid.md
+++ b/man/osmium-getid.md
@@ -6,51 +6,92 @@ osmium-getid - get objects from OSM file by ID
 
 # SYNOPSIS
 
-**osmium getid** \[*OPTIONS*\] *INPUT-FILE* *ID*...
+**osmium getid** \[*OPTIONS*\] *OSM-FILE* *ID*...\
+**osmium getid** \[*OPTIONS*\] *OSM-FILE* -i *ID-FILE*\
+**osmium getid** \[*OPTIONS*\] *OSM-FILE* -I *ID-OSM-FILE*
 
 
 # DESCRIPTION
 
-Get objects with the given IDs from the input and write them to the output
+Get objects with the given IDs from the input and write them to the output.
 
+IDs can be given on the command line (first case in synopsis), or read from
+a text file with one ID per line (second case in synopsis), or read from an
+OSM file (third cases in synopsis).
 
-# OPTIONS
+All objects with these IDs will be read from *OSM-FILE* and written to the
+output. If the option **-r**, **--add-referenced** is used all objects
+referenced from those objects will also be added to the output.
+
+Objects will be written out in the order they are found in the *OSM-FILE*.
+
+If the option **-r**, **--add-referenced** is *not* used, the input file is
+read only once, if it is used, the input file will possibly be read up to
+three times.
+
+On the command line or in the ID file, the IDs have the form: *TYPE-LETTER*
+*NUMBER*. The type letter is 'n' for nodes, 'w' for ways, and 'r' for
+relations. If there is no type letter, 'n' for nodes is assumed (or whatever
+the **--default-type** option says). So "n13 w22 17 r21" will match the nodes
+13 and 17, the way 22 and the relation 21.
+
+The order in which the IDs appear does not matter.
+
+On the command line, the list of IDs can be in separate arguments or in a
+single argument separated by spaces, tabs, commas (,), semicolons (;), forward
+slashes (/) or pipe characters (|).
+
+In an ID file (option **-i**/**--id-file**) each line must start with an ID in
+the format described above. Lines can optionally contain a space character or a
+hash sign ('#') after the ID. Any characters after that are ignored. (This also
+allows files in OPL format to be read.) Empty lines are ignored.
 
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    See **osmium-file-formats**(5) or the libosmium manual for details.
+Note that all objects will be taken from the *OSM-FILE*, the *ID-OSM-FILE* is
+only used to detect which objects to get. This might matter if there are
+different object versions in the different files.
 
--F, --input-format=FORMAT
-:   The format of the input files. Can be used to set the input format if it
-    can't be autodetected from the file names. This will set the format for
-    all input files, there is no way to set the format for some input files
-    only. See **osmium-file-formats**(5) or the libosmium manual for details.
+The *OSM-FILE* can not be a history file unless the **-H**, **--history**
+option is used. Then all versions of the objects will be copied to the ouput.
 
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
+If referenced objects are missing from the input file, the type and IDs
+of those objects is written out to *stderr* at the end of the program unless
+the **-H**, **--history** option was given.
 
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
 
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
+# OPTIONS
+
+--default-type=TYPE
+:   Use TYPE ('node', 'way', or 'relation') for IDs without a type prefix
+    (default: 'node'). It is also allowed to just use the first character
+    of the type here.
+
+-H, --history
+:   Make this program work on history files. This is only needed when using
+    the **-r** option.
+
+-i, --id-file[=FILE]
+:   Read IDs from text file instead of from the command line. Use the special
+    name "-" to read from *stdin*. Each line of the file must start with an
+    ID in the format described above. Lines can optionally contain a space
+    character or a hash sign ('#') after the ID. This character and all
+    following characters are ignored. (This allows files in OPL format to be
+    read.) Empty lines are also ignored.
 
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
+-I, --id-osm-file=OSMFILE
+:   Like **-i** but get the IDs from an OSM file.
 
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
+-r, --add-referenced
+:   Recursively find all objects referenced by the objects of the given IDs
+    and include them in the output. This only works correctly on non-history
+    files unless the `-H` option is also used.
 
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
+--verbose-ids
+:   Also print all requested and missing IDs. This is usually disabled, because
+    the lists can get quite long. (This option implies `--verbose`.)
 
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -60,12 +101,19 @@ Get objects with the given IDs from the input and write them to the output
   ~ if all IDs were found
 
 1
-  ~ if there was an error processing the data or not all IDs were found, or
+  ~ if there was an error processing the data or not all IDs were found,
+    (this is only detected if the **-h**, **--history** option was not used),
 
 2
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium getid** does all its work on the fly and only keeps a table of all
+IDs it needs in main memory.
+
+
 # EXAMPLES
 
 Output nodes 17 and 1234, way 42, and relation 111 to *stdout* in OPL format:
diff --git a/man/osmium-merge-changes.md b/man/osmium-merge-changes.md
index 0e329b0..842c98d 100644
--- a/man/osmium-merge-changes.md
+++ b/man/osmium-merge-changes.md
@@ -6,58 +6,27 @@ osmium-merge-changes - merge several OSM change files into one
 
 # SYNOPSIS
 
-**osmium merge-changes** \[*OPTIONS*\] *CHANGE-FILE*...
+**osmium merge-changes** \[*OPTIONS*\] *OSM-CHANGE-FILE*...
 
 
 # DESCRIPTION
 
 Merges the content of all change files given on the command line into one large
-change file. Objects are sorted by type, ID, and version, so it doesn't matter
-in what order the change files are given or in what order they contain the data.
-
-**osmium merge** does its work in main memory, so all data has to fit in there!
+change file. Objects are sorted by type, ID, version, and timestamp so it
+doesn't matter in what order the change files are given or in what order they
+contain the data.
 
 
 # OPTIONS
 
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    See **osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input files. Can be used to set the input format if it
-    can't be autodetected from the file names. This will set the format for
-    all input files, there is no way to set the format for some input files
-    only. See **osmium-file-formats**(5) or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
-
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
-
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
-
 -s, --simplify
-:   Only write the last version of any object to the output.
-
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
+:   Only write the last version of any object to the output. For an object
+    created in one of the change files and removed in a later one, the deleted
+    version of the object will still appear because it is the latest version.
 
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -73,6 +42,13 @@ in what order the change files are given or in what order they contain the data.
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium merge-changes** keeps the contents of all the change files in main
+memory. This will take roughly 10 times as much memory as the files take on
+disk in *.osm.bz2* format.
+
+
 # EXAMPLES
 
 Merge all changes in *changes* directory into *all.osc.gz*:
diff --git a/man/osmium-renumber.md b/man/osmium-renumber.md
index 48601ca..0b8d393 100644
--- a/man/osmium-renumber.md
+++ b/man/osmium-renumber.md
@@ -6,7 +6,7 @@ osmium-renumber - renumber object IDs
 
 # SYNOPSIS
 
-**osmium renumber** \[*OPTIONS*\] *INPUT-FILE*
+**osmium renumber** \[*OPTIONS*\] *OSM-DATA-FILE*
 
 
 # DESCRIPTION
@@ -14,15 +14,13 @@ osmium-renumber - renumber object IDs
 The objects (nodes, ways, and relations) in an OSM file often have very large
 IDs. This can make some kinds of postprocessing difficult. This command will
 renumber all objects using IDs starting at 1. Referential integrity will be
-kept.
+kept. All objects which appear in the source file will be in the same order
+in the output file. IDs of objects which are not in the file but referenced
+from ways or relations are not guaranteed to be in the correct order.
 
-This command can only be run on OSM files sorted in the usual way (nodes first,
-then ways, then IDs). It will read the input file twice, so it will not work
-with STDIN.
-
-This command needs quite a bit of main memory to keep the mapping between old
-and new IDs. It is intended for small extracts. Don't try to run this on a full
-planet!
+This command expects the input file to be ordered in the usual way: First
+nodes in order of ID, then ways in order of ID, then relations in order of ID.
+The input file will be read twice, so it will not work with STDIN.
 
 You must never upload the data generated by this command to OSM! This would
 really confuse the OSM database because it knows the objects under different
@@ -31,21 +29,6 @@ IDs.
 
 # OPTIONS
 
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    **See osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input file. Can be used to set the input format if it
-    can't be autodetected from the file name. See **osmium-file-formats**(5)
-    or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the
-    version of osmium.
-
 -i, --index-directory=DIR
 :   Directory where the index files for mapping between old and news IDs are
     read from and written to, respectively. Use this if you want to map IDs
@@ -54,25 +37,9 @@ IDs.
     directory. The files written will be named `nodes.idx`, `ways.idx`, and
     `relations.idx`.
 
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
-
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
-
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -88,14 +55,24 @@ IDs.
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium renumber** needs quite a bit of main memory to keep the mapping
+between old and new IDs. It is intended for small to medium sized extracts.
+You will need more than 32 GB RAM to run this on a full planet.
+
+Memory use is at least 8 bytes per node, way, and relation ID in the input
+file.
+
+
 # EXAMPLES
 
 Renumber a PBF file and output to a compressed XML file:
 
-    osmium renumber -o ch.osm.bz2 switzerland.osm.pbf
+    osmium renumber -o ch.osm.bz2 germany.osm.pbf
 
-Renumbering Switzerland currently (summer 2015) takes only about a minute and
-needs a bit more than 2 GB RAM.
+Renumbering Germany currently (spring 2016) takes less than three minutes and
+needs about 3 GB RAM.
 
 Renumber an OSM file storing the indexes on disk:
 
diff --git a/man/osmium-show.md b/man/osmium-show.md
new file mode 100644
index 0000000..5da5c35
--- /dev/null
+++ b/man/osmium-show.md
@@ -0,0 +1,95 @@
+
+# NAME
+
+osmium-show - show OSM file
+
+
+# SYNOPSIS
+
+**osmium show** \[*OPTIONS*\] *OSM-FILE*
+
+
+# DESCRIPTION
+
+Show the contents of the *OSM-FILE* on *stdout*, usually in a pager. The
+output format can be set using the **-f**, **output-format** option, its
+shortcuts **-d** (debug format with colors), **-o** (OPL), or **-x** (XML),
+or the `OSMIUM_SHOW_FORMAT` environment variable.
+
+The pager can be set with the `OSMIUM_PAGER` or the `PAGER` environment
+variable. If neither is set, the default `less` is used unless the option
+`--no-pager` is used. If the pager variables are set to an empty value or
+to `cat`, no pager is used. On Windows there is no pager support at all.
+
+
+# OPTIONS
+
+-f, --output-format=FORMAT
+:   The format of the output file. Can be used to set the output file format
+    if it can't be autodetected from the output file name.
+    **See osmium-file-formats**(5) or the libosmium manual for details.
+
+--no-pager
+:   Disable pager.
+
+-d, --format-debug
+:   Same as `-f debug,color=true`.
+
+-o, --format-opl
+:   Same as `-f opl`.
+
+-x, --format-xml
+:   Same as `-f xml`.
+
+-t, --object-type=TYPE
+:   Read only objects of given type (*node*, *way*, *relation*, *changeset*).
+    By default all types are read. This option can be given multiple times.
+
+
+# COMMON OPTIONS
+
+-h, --help
+:   Show usage help.
+
+ at MAN_INPUT_OPTIONS@
+
+# DIAGNOSTICS
+
+**osmium show** exits with exit code
+
+0
+  ~ if everything went alright,
+
+1
+  ~ if there was an error processing the data, or
+
+2
+  ~ if there was a problem with the command line arguments.
+
+
+# MEMORY USAGE
+
+**osmium show** does all its work on the fly and doesn't keep much data in
+main memory.
+
+
+# EXAMPLES
+
+Show an OSM file using the default pager and default format:
+
+    osmium show norway.osm.pbf
+
+Use `more` as a pager and only show relations:
+
+    OSMIUM_PAGER=more osmium show -r norway.osm.pbf
+
+Show using XML format:
+
+    osmium show -x norway.osm.pbf
+
+
+# SEE ALSO
+
+* **osmium**(1), **osmium-cat**(1), **osmium-file-formats**(5)
+* [Osmium website](http://osmcode.org/osmium)
+
diff --git a/man/osmium-sort.md b/man/osmium-sort.md
index e65c063..9861893 100644
--- a/man/osmium-sort.md
+++ b/man/osmium-sort.md
@@ -6,7 +6,7 @@ osmium-sort - sort OSM files
 
 # SYNOPSIS
 
-**osmium sort** \[*OPTIONS*\] *INPUT-FILE*...
+**osmium sort** \[*OPTIONS*\] *OSM-FILE*...
 
 
 # DESCRIPTION
@@ -16,46 +16,9 @@ result. Objects are sorted by type, ID, and version.
 
 This works with normal OSM data files, history files, and change files.
 
-**osmium sort** does its work in main memory, so all data has to fit in there!
-
-
-# OPTIONS
-
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    See **osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input files. Can be used to set the input format if it
-    can't be autodetected from the file names. This will set the format for
-    all input files, there is no way to set the format for some input files
-    only. See **osmium-file-formats**(5) or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
-
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
-
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
-
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -71,6 +34,13 @@ This works with normal OSM data files, history files, and change files.
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium sort** keeps the contents of all the input files in main memory. This
+will take roughly 10 times as much memory as the files take on disk in
+*.osm.bz2* or *osm.pbf* format.
+
+
 # EXAMPLES
 
 Sort *in.osm.bz2* and write out to *sorted.osm.pbf*:
diff --git a/man/osmium-time-filter.md b/man/osmium-time-filter.md
index 33bbf0e..b0b3cdc 100644
--- a/man/osmium-time-filter.md
+++ b/man/osmium-time-filter.md
@@ -6,8 +6,8 @@ osmium-time-filter - filter OSM data by time from a history file
 
 # SYNOPSIS
 
-**osmium time-filter** \[*OPTIONS*\] *INPUT-FILE* \[*TIME*\]\
-**osmium time-filter** \[*OPTIONS*\] *INPUT-FILE* *FROM-TIME* *TO-TIME*
+**osmium time-filter** \[*OPTIONS*\] *OSM-HISTORY-FILE* \[*TIME*\]\
+**osmium time-filter** \[*OPTIONS*\] *OSM-HISTORY-FILE* *FROM-TIME* *TO-TIME*
 
 
 # DESCRIPTION
@@ -25,43 +25,9 @@ without history containing no deleted objects.
 
 The format for the timestamps is "yyyy-mm-ddThh:mm::ssZ".
 
-
-# OPTIONS
-
--f, --output-format=FORMAT
-:   The format of the output file. Can be used to set the output file format
-    if it can't be autodetected from the output file name.
-    **See osmium-file-formats**(5) or the libosmium manual for details.
-
--F, --input-format=FORMAT
-:   The format of the input file. Can be used to set the input file format
-    if it can't be autodetected from the file name.
-    **See osmium-file-formats**(5) or the libosmium manual for details.
-
---generator=NAME
-:   The name and version of the program generating the output file. It will be
-    added to the header of the output file. Default is "*osmium/*" and the version
-    of osmium.
-
--o, --output=FILE
-:   Name of the output file. Default is '-' (*stdout*).
-
---output-header=OPTION
-:   Add output header option. This option can be given several times. See the
-    *libosmium manual* for a list of allowed header options.
-
--O, --overwrite
-:   Allow an existing output file to be overwritten. Normally **osmium** will
-    refuse to write over an existing file.
-
---fsync
-:   Call fsync after writing the output file to force the OS to flush buffers
-    to disk.
-
--v, --verbose
-:   Set verbose mode. The program will output information about what it is
-    doing to *stderr*.
-
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
 
 # DIAGNOSTICS
 
@@ -77,6 +43,12 @@ The format for the timestamps is "yyyy-mm-ddThh:mm::ssZ".
   ~ if there was a problem with the command line arguments.
 
 
+# MEMORY USAGE
+
+**osmium time-filter** does all its work on the fly and doesn't keep much data
+in main memory.
+
+
 # EXAMPLES
 
 Extract current planet file from history planet:
diff --git a/man/osmium.md b/man/osmium.md
index a3c9fda..d4855ba 100644
--- a/man/osmium.md
+++ b/man/osmium.md
@@ -27,6 +27,9 @@ Run **osmium help** *COMMAND* to get more information about a command.
 
 # COMMANDS
 
+add-locations-to-ways
+:   add node locations to ways in OSM file
+
 apply-changes
 :   apply OSM change file(s) to OSM data file
 
@@ -54,6 +57,9 @@ merge-changes
 renumber
 :   renumber object IDs
 
+show
+:   show OSM file
+
 sort
 :   sort OSM files
 
@@ -61,15 +67,46 @@ time-filter
 :   filter OSM data by time from a history file
 
 
+# COMMON OPTIONS
+
+Most commands support the following options:
+
+-h, --help
+:   Show short usage information.
+
+-v, --verbose
+:   Set verbose mode. The program will output information about what it is
+    doing to *stderr*.
+
+
+# MEMORY USAGE
+
+Osmium commands try to do their work as memory efficient as possible. But some
+osmium commands still need to load quite a bit of data into main memory. In
+some cases this means that only smaller datasets can be handled. Look into the
+man pages for the individual commands to learn more about their memory use.
+
+If you use the **-v**, **--verbose** option on most commands they will print
+out their peak memory usage at the end. This is the actual amount of memory
+used including the program code itself, any needed libraries and the data.
+(Printing of memory usage is currently only available on Linux systems.)
+
+If an osmium command exits with an "Out of memory" error, try running it with
+**--verbose** on smaller datasets to get an idea how much memory it needs.
+
+
 # SEE ALSO
 
 * **osmium-apply-changes**(1),
   **osmium-cat**(1),
+  **osmium-changeset-filter**(1),
   **osmium-check-refs**(1),
   **osmium-fileinfo**(1),
   **osmium-getid**(1),
   **osmium-merge-changes**(1),
   **osmium-renumber**(1),
+  **osmium-show**(1),
+  **osmium-sort**(1),
   **osmium-time-filter**(1),
   **osmium-file-formats**(5)
 * [Osmium website](http://osmcode.org/osmium)
diff --git a/man/output-options.md b/man/output-options.md
new file mode 100644
index 0000000..c6100a3
--- /dev/null
+++ b/man/output-options.md
@@ -0,0 +1,26 @@
+
+# OUTPUT OPTIONS
+
+-f, --output-format=FORMAT
+:   The format of the output file. Can be used to set the output file format
+    if it can't be autodetected from the output file name.
+    See **osmium-file-formats**(5) or the libosmium manual for details.
+
+--fsync
+:   Call fsync after writing the output file to force flushing buffers to disk.
+
+--generator=NAME
+:   The name and version of the program generating the output file. It will be
+    added to the header of the output file. Default is "*osmium/*" and the
+    version of osmium.
+
+-o, --output=FILE
+:   Name of the output file. Default is '-' (*stdout*).
+
+-O, --overwrite
+:   Allow an existing output file to be overwritten. Normally **osmium** will
+    refuse to write over an existing file.
+
+--output-header=OPTION
+:   Add output header option. This option can be given several times. See the
+    *libosmium manual* for a list of allowed header options.
diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh
deleted file mode 100755
index d11ac79..0000000
--- a/scripts/travis_script.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh
-#
-#  travis_script.sh
-#
-
-mkdir build
-cd build
-
-# GCC ignores the pragmas in the code that disable the "return-type" warning
-# selectively, so use this workaround.
-if [ "${CXX}" = "g++" ]; then
-    WORKAROUND="-DCMAKE_CXX_FLAGS=-Wno-return-type"
-else
-    WORKAROUND=""
-fi
-
-if [ "${CXX}" = "g++" ]; then
-    CXX=g++-4.8
-    CC=gcc-4.8
-fi
-
-echo "travis_fold:start:cmake\nRunning cmake..."
-cmake -LA \
-    -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-    ${WORKAROUND} \
-    ..
-echo "travis_fold:end:cmake"
-
-echo "travis_fold:start:make\nRunning make..."
-make VERBOSE=1
-echo "travis_fold:end:make"
-
-echo "travis_fold:start:ctest\nRunning ctest..."
-ctest --output-on-failure
-echo "travis_fold:end:ctest"
-
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f910154..a4f7197 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,7 +10,7 @@ file(GLOB OSMIUM_SOURCE_FILES *.cpp)
 
 add_executable(osmium ${OSMIUM_SOURCE_FILES})
 
-target_link_libraries(osmium ${Boost_LIBRARIES} ${CRYPTOPP_LIBRARIES} ${OSMIUM_LIBRARIES})
+target_link_libraries(osmium ${Boost_LIBRARIES} ${OSMIUM_LIBRARIES})
 
 install(TARGETS osmium DESTINATION bin)
 
diff --git a/src/cmd.cpp b/src/cmd.cpp
new file mode 100644
index 0000000..4c0e215
--- /dev/null
+++ b/src/cmd.cpp
@@ -0,0 +1,84 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium
+
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/util/memory.hpp>
+
+#include "cmd.hpp"
+#include "exception.hpp"
+
+po::options_description Command::add_common_options() {
+    po::options_description options("COMMON OPTIONS");
+
+    options.add_options()
+    ("help,h", "Show usage help")
+    ("verbose,v", "Set verbose mode")
+    ;
+
+    return options;
+}
+
+void Command::setup_common(const boost::program_options::variables_map& vm, const po::options_description& desc) {
+    if (vm.count("help")) {
+        std::cout << "Usage: " << synopsis() << "\n\n"
+                  << CommandFactory::get_description(name()) << "\n"
+                  << desc
+                  << "\nUse 'osmium help " << name() << "' to display the manual page.\n";
+        exit(0);
+    }
+
+    if (vm.count("verbose")) {
+        m_vout.verbose(true);
+    }
+
+    if (vm.count("object-type")) {
+        m_osm_entity_bits = osmium::osm_entity_bits::nothing;
+        for (const auto& t : vm["object-type"].as<std::vector<std::string>>()) {
+            if (t == "n" || t == "node") {
+                m_osm_entity_bits |= osmium::osm_entity_bits::node;
+            } else if (t == "w" || t == "way") {
+                m_osm_entity_bits |= osmium::osm_entity_bits::way;
+            } else if (t == "r" || t == "relation") {
+                m_osm_entity_bits |= osmium::osm_entity_bits::relation;
+            } else if (t == "c" || t == "changeset") {
+                m_osm_entity_bits |= osmium::osm_entity_bits::changeset;
+            } else {
+                throw argument_error(std::string("Unknown object type '") + t + "' (Allowed are 'node', 'way', 'relation', and 'changeset').");
+            }
+        }
+    }
+}
+
+void Command::print_arguments(const std::string& command) {
+    if (m_vout.verbose()) {
+        m_vout << "Started osmium " << command << "\n";
+        m_vout << "Command line options and default settings:\n";
+        show_arguments();
+    }
+}
+
+void Command::show_memory_used() {
+    osmium::MemoryUsage mem;
+    if (mem.current() > 0) {
+        m_vout << "Peak memory used: " << mem.peak() << " MBytes\n";
+    }
+}
+
diff --git a/src/cmd.hpp b/src/cmd.hpp
index f08c238..b107855 100644
--- a/src/cmd.hpp
+++ b/src/cmd.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@ namespace po = boost::program_options;
 
 #include <osmium/io/file.hpp>
 #include <osmium/io/writer_options.hpp>
+#include <osmium/osm/entity_bits.hpp>
 #include <osmium/util/minmax.hpp>
 #include <osmium/util/verbose_output.hpp>
 
@@ -42,6 +43,8 @@ namespace po = boost::program_options;
  */
 class Command {
 
+    osmium::osm_entity_bits::type m_osm_entity_bits {osmium::osm_entity_bits::all};
+
 protected:
 
     bool m_debug {false};
@@ -78,24 +81,19 @@ public:
     // It returns false if there was an error.
     virtual bool run() = 0;
 
-    void add_common_options(po::options_description& options) {
-        options.add_options()
-        ("verbose,v", "Set verbose mode")
-        ;
-    }
+    // The name of the command.
+    virtual const char* name() const noexcept = 0;
 
-    void setup_common(const boost::program_options::variables_map& vm) {
-        if (vm.count("verbose")) {
-            m_vout.verbose(true);
-        }
-    }
+    // The command line usage synopsis of the command.
+    virtual const char* synopsis() const noexcept = 0;
+
+    po::options_description add_common_options();
+    void setup_common(const boost::program_options::variables_map& vm, const po::options_description& desc);
+    void print_arguments(const std::string& command);
+    void show_memory_used();
 
-    void print_arguments(const std::string& command) {
-        if (m_vout.verbose()) {
-            m_vout << "Started osmium " << command << "\n";
-            m_vout << "Command line options and default settings:\n";
-            show_arguments();
-        }
+    osmium::osm_entity_bits::type osm_entity_bits() const {
+        return m_osm_entity_bits;
     }
 
 }; // class Command
@@ -112,7 +110,7 @@ public:
 
     void setup_input_file(const boost::program_options::variables_map& vm);
 
-    void add_single_input_options(po::options_description& options);
+    po::options_description add_single_input_options();
 
     void show_single_input_arguments(osmium::util::VerboseOutput& vout);
 
@@ -132,9 +130,9 @@ protected:
 
 public:
 
-    void setup_input_files(const boost::program_options::variables_map& vm);
+    void setup_input_files(const boost::program_options::variables_map& vm, bool optional = false);
 
-    void add_multiple_inputs_options(po::options_description& options);
+    po::options_description add_multiple_inputs_options();
 
     void show_multiple_inputs_arguments(osmium::util::VerboseOutput& vout);
 
@@ -164,7 +162,7 @@ public:
 
     void setup_output_file(const po::variables_map& vm);
 
-    void add_output_options(po::options_description& options);
+    po::options_description add_output_options();
 
     void show_output_arguments(osmium::util::VerboseOutput& vout);
 
diff --git a/src/cmd_factory.cpp b/src/cmd_factory.cpp
index 62c3cdf..7390a2e 100644
--- a/src/cmd_factory.cpp
+++ b/src/cmd_factory.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_add_locations_to_ways.cpp b/src/command_add_locations_to_ways.cpp
new file mode 100644
index 0000000..a70a5de
--- /dev/null
+++ b/src/command_add_locations_to_ways.cpp
@@ -0,0 +1,173 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium
+
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <iostream>
+#include <iterator>
+
+#include <boost/program_options.hpp>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/io/any_output.hpp>
+
+#include "command_add_locations_to_ways.hpp"
+#include "exception.hpp"
+
+bool CommandAddLocationsToWays::setup(const std::vector<std::string>& arguments) {
+    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    std::string default_index_type{ map_factory.has_map_type("sparse_mmap_array") ? "sparse_mmap_array" : "sparse_mem_array" };
+
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
+    ("index-type,i", po::value<std::string>()->default_value(default_index_type), "Index type to use")
+    ("show-index-types,I", "Show available index types")
+    ("keep-untagged-nodes,n", "Keep untagged nodes")
+    ;
+
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_multiple_inputs_options()};
+    po::options_description opts_output{add_output_options()};
+
+    po::options_description hidden;
+    hidden.add_options()
+    ("input-filenames", po::value<std::vector<std::string>>(), "Input files")
+    ;
+
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
+
+    po::positional_options_description positional;
+    positional.add("input-filenames", -1);
+
+    po::variables_map vm;
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
+    po::notify(vm);
+
+    if (vm.count("show-index-types")) {
+        for (const auto& map_type : map_factory.map_types()) {
+            std::cout << map_type << "\n";
+        }
+        return false;
+    }
+
+    if (vm.count("index-type")) {
+        m_index_type_name = vm["index-type"].as<std::string>();
+        if (!map_factory.has_map_type(m_index_type_name)) {
+            throw argument_error(std::string{"Unknown index type '"} + m_index_type_name + "'. Use --show-index-types or -I to get a list.");
+        }
+    }
+
+    setup_common(vm, desc);
+    setup_input_files(vm);
+    setup_output_file(vm);
+
+    if (vm.count("keep-untagged-nodes")) {
+        m_keep_untagged_nodes = true;
+    }
+
+    return true;
+}
+
+void CommandAddLocationsToWays::show_arguments() {
+    show_multiple_inputs_arguments(m_vout);
+    show_output_arguments(m_vout);
+
+    m_vout << "  other options:\n";
+    m_vout << "    index type: " << m_index_type_name << "\n";
+    m_vout << "    keep untagged nodes: " << (m_keep_untagged_nodes ? "yes" : "no") << "\n";
+    m_vout << "\n";
+}
+
+void CommandAddLocationsToWays::setup_header(osmium::io::Header& header) const {
+    header.set("generator", m_generator);
+    for (const auto& h : m_output_headers) {
+        header.set(h);
+    }
+}
+
+void CommandAddLocationsToWays::copy_data(osmium::io::Reader& reader, osmium::io::Writer& writer, location_handler_type& location_handler) {
+    while (osmium::memory::Buffer buffer = reader.read()) {
+        osmium::apply(buffer, location_handler);
+
+        if (m_keep_untagged_nodes) {
+            writer(std::move(buffer));
+        } else {
+            for (const auto& object : buffer) {
+                if (object.type() != osmium::item_type::node || !static_cast<const osmium::Node&>(object).tags().empty()) {
+                    writer(object);
+                }
+            }
+        }
+    }
+}
+
+bool CommandAddLocationsToWays::run() {
+    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    auto location_index = map_factory.create_map(m_index_type_name);
+    location_handler_type location_handler(*location_index);
+    location_handler.ignore_errors(); // XXX
+
+    m_output_file.set("locations_on_ways");
+
+    if (m_input_files.size() == 1) { // single input file
+        m_vout << "Copying input file '" << m_input_files[0].filename() << "'\n";
+        osmium::io::Reader reader(m_input_files[0]);
+        osmium::io::Header header = reader.header();
+        setup_header(header);
+        osmium::io::Writer writer(m_output_file, header, m_output_overwrite, m_fsync);
+
+        copy_data(reader, writer, location_handler);
+
+        writer.close();
+        reader.close();
+    } else { // multiple input files
+        osmium::io::Header header;
+        setup_header(header);
+        osmium::io::Writer writer(m_output_file, header, m_output_overwrite, m_fsync);
+
+        for (const auto& input_file : m_input_files) {
+            m_vout << "Copying input file '" << input_file.filename() << "'\n";
+            osmium::io::Reader reader(input_file);
+
+            copy_data(reader, writer, location_handler);
+
+            reader.close();
+        }
+        writer.close();
+    }
+
+    show_memory_used();
+    m_vout << "Done.\n";
+
+    return true;
+}
+
+namespace {
+
+    const bool register_add_locations_to_ways_command = CommandFactory::add("add-locations-to-ways", "Add node locations to ways", []() {
+        return new CommandAddLocationsToWays();
+    });
+
+}
+
diff --git a/src/command_add_locations_to_ways.hpp b/src/command_add_locations_to_ways.hpp
new file mode 100644
index 0000000..113b4cf
--- /dev/null
+++ b/src/command_add_locations_to_ways.hpp
@@ -0,0 +1,76 @@
+#ifndef COMMAND_ADD_LOCATIONS_TO_WAYS_HPP
+#define COMMAND_ADD_LOCATIONS_TO_WAYS_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium
+
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <string>
+#include <vector>
+
+#include <osmium/index/map/dense_mem_array.hpp>
+#include <osmium/index/map/dense_mmap_array.hpp>
+#include <osmium/index/map/sparse_mem_array.hpp>
+#include <osmium/index/map/sparse_mmap_array.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/osm/entity_bits.hpp>
+
+#include "cmd.hpp"
+
+using index_type = osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
+using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
+
+namespace osmium { namespace io {
+    class Reader;
+    class Writer;
+} }
+
+class CommandAddLocationsToWays : public Command, public with_multiple_osm_inputs, public with_osm_output {
+
+    void setup_header(osmium::io::Header& header) const;
+    void copy_data(osmium::io::Reader& reader, osmium::io::Writer& writer, location_handler_type& location_handler);
+
+    std::string m_index_type_name;
+    bool m_keep_untagged_nodes = false;
+
+public:
+
+    CommandAddLocationsToWays() = default;
+
+    bool setup(const std::vector<std::string>& arguments) override final;
+
+    void show_arguments() override final;
+
+    bool run() override final;
+
+    const char* name() const noexcept override final {
+        return "add-locations-to-ways";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium add-locations-to-ways [OPTIONS] OSM-FILE...";
+    }
+
+}; // class CommandAddLocationsToWays
+
+
+#endif // COMMAND_ADD_LOCATIONS_TO_WAYS_HPP
diff --git a/src/command_apply_changes.cpp b/src/command_apply_changes.cpp
index 1f33bb3..4911363 100644
--- a/src/command_apply_changes.cpp
+++ b/src/command_apply_changes.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -32,34 +32,37 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "command_apply_changes.hpp"
 
 bool CommandApplyChanges::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("simplify,s", "Simplify change")
     ("remove-deleted,r", "Remove deleted objects from output")
     ;
 
-    add_common_options(cmdline);
-    add_single_input_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "OSM input file")
     ("change-filenames", po::value<std::vector<std::string>>(), "OSM change input files")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
     positional.add("change-filenames", -1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_file(vm);
     setup_output_file(vm);
 
@@ -185,6 +188,7 @@ bool CommandApplyChanges::run() {
     writer.close();
     reader.close();
 
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_apply_changes.hpp b/src/command_apply_changes.hpp
index 78d0638..e89e866 100644
--- a/src/command_apply_changes.hpp
+++ b/src/command_apply_changes.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -45,6 +45,14 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "apply-changes";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium apply-changes [OPTIONS] OSM-DATA-FILE OSM-CHANGE-FILE...";
+    }
+
 }; // class CommandApplyChanges
 
 
diff --git a/src/command_cat.cpp b/src/command_cat.cpp
index 74a87ad..0da1415 100644
--- a/src/command_cat.cpp
+++ b/src/command_cat.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -32,51 +32,37 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "exception.hpp"
 
 bool CommandCat::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("object-type,t", po::value<std::vector<std::string>>(), "Read only objects of given type (node, way, relation, changeset)")
     ;
 
-    add_common_options(cmdline);
-    add_multiple_inputs_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_multiple_inputs_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filenames", po::value<std::vector<std::string>>(), "Input files")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filenames", -1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_files(vm);
     setup_output_file(vm);
 
-    if (vm.count("object-type")) {
-        m_osm_entity_bits = osmium::osm_entity_bits::nothing;
-        for (const auto& t : vm["object-type"].as<std::vector<std::string>>()) {
-            if (t == "node") {
-                m_osm_entity_bits |= osmium::osm_entity_bits::node;
-            } else if (t == "way") {
-                m_osm_entity_bits |= osmium::osm_entity_bits::way;
-            } else if (t == "relation") {
-                m_osm_entity_bits |= osmium::osm_entity_bits::relation;
-            } else if (t == "changeset") {
-                m_osm_entity_bits |= osmium::osm_entity_bits::changeset;
-            } else {
-                throw argument_error(std::string("Unknown object type '") + t + "' (Allowed are 'node', 'way', 'relation', and 'changeset').");
-            }
-        }
-    }
-
     return true;
 }
 
@@ -86,16 +72,16 @@ void CommandCat::show_arguments() {
 
     m_vout << "  other options:\n";
     m_vout << "    object types:";
-    if (m_osm_entity_bits & osmium::osm_entity_bits::node) {
+    if (osm_entity_bits() & osmium::osm_entity_bits::node) {
         m_vout << " node";
     }
-    if (m_osm_entity_bits & osmium::osm_entity_bits::way) {
+    if (osm_entity_bits() & osmium::osm_entity_bits::way) {
         m_vout << " way";
     }
-    if (m_osm_entity_bits & osmium::osm_entity_bits::relation) {
+    if (osm_entity_bits() & osmium::osm_entity_bits::relation) {
         m_vout << " relation";
     }
-    if (m_osm_entity_bits & osmium::osm_entity_bits::changeset) {
+    if (osm_entity_bits() & osmium::osm_entity_bits::changeset) {
         m_vout << " changeset";
     }
     m_vout << "\n";
@@ -111,7 +97,7 @@ void CommandCat::setup_header(osmium::io::Header& header) const {
 bool CommandCat::run() {
     if (m_input_files.size() == 1) { // single input file
         m_vout << "Copying input file '" << m_input_files[0].filename() << "'\n";
-        osmium::io::Reader reader(m_input_files[0], m_osm_entity_bits);
+        osmium::io::Reader reader(m_input_files[0], osm_entity_bits());
         osmium::io::Header header = reader.header();
         setup_header(header);
         osmium::io::Writer writer(m_output_file, header, m_output_overwrite, m_fsync);
@@ -128,7 +114,7 @@ bool CommandCat::run() {
 
         for (const auto& input_file : m_input_files) {
             m_vout << "Copying input file '" << input_file.filename() << "'\n";
-            osmium::io::Reader reader(input_file, m_osm_entity_bits);
+            osmium::io::Reader reader(input_file, osm_entity_bits());
             while (osmium::memory::Buffer buffer = reader.read()) {
                 writer(std::move(buffer));
             }
@@ -137,6 +123,7 @@ bool CommandCat::run() {
         writer.close();
     }
 
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_cat.hpp b/src/command_cat.hpp
index 508879b..0abeaf6 100644
--- a/src/command_cat.hpp
+++ b/src/command_cat.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 class CommandCat : public Command, public with_multiple_osm_inputs, public with_osm_output {
 
-    osmium::osm_entity_bits::type m_osm_entity_bits = osmium::osm_entity_bits::all;
+    void setup_header(osmium::io::Header& header) const;
 
 public:
 
@@ -45,10 +45,12 @@ public:
 
     bool run() override final;
 
-    void setup_header(osmium::io::Header& header) const;
+    const char* name() const noexcept override final {
+        return "cat";
+    }
 
-    osmium::osm_entity_bits::type osm_entity_bits() const {
-        return m_osm_entity_bits;
+    const char* synopsis() const noexcept override final {
+        return "osmium cat [OPTIONS] OSM-FILE...";
     }
 
 }; // class CommandCat
diff --git a/src/command_changeset_filter.cpp b/src/command_changeset_filter.cpp
index 952551b..d5e656a 100644
--- a/src/command_changeset_filter.cpp
+++ b/src/command_changeset_filter.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -33,8 +33,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "exception.hpp"
 
 bool CommandChangesetFilter::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("with-discussion,d", "Changesets with discussions (comments)")
     ("without-discussion,D", "Changesets without discussions (no comments)")
     ("with-changes,c", "Changesets with changes")
@@ -47,26 +47,29 @@ bool CommandChangesetFilter::setup(const std::vector<std::string>& arguments) {
     ("before,b", po::value<std::string>(), "Changesets closed before this time")
     ;
 
-    add_common_options(cmdline);
-    add_single_input_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "OSM input file")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_file(vm);
     setup_output_file(vm);
 
@@ -217,6 +220,7 @@ bool CommandChangesetFilter::run() {
     writer.close();
     reader.close();
 
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_changeset_filter.hpp b/src/command_changeset_filter.hpp
index 9bd5096..b861513 100644
--- a/src/command_changeset_filter.hpp
+++ b/src/command_changeset_filter.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -54,6 +54,14 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "changeset-filter";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium changeset-filter [OPTIONS] OSM-CHANGESET-FILE";
+    }
+
 }; // class CommandChangesetFilter
 
 
diff --git a/src/command_check_refs.cpp b/src/command_check_refs.cpp
index 3705e07..37d002c 100644
--- a/src/command_check_refs.cpp
+++ b/src/command_check_refs.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <boost/program_options.hpp>
 
+#include <osmium/handler/check_order.hpp>
 #include <osmium/index/bool_vector.hpp>
 #include <osmium/io/any_input.hpp>
 #include <osmium/io/any_output.hpp>
@@ -35,31 +36,34 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "command_check_refs.hpp"
 
 bool CommandCheckRefs::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("show-ids,i", "Show IDs of missing objects")
     ("check-relations,r", "Also check relations")
     ;
 
-    add_common_options(cmdline);
-    add_single_input_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "Input file")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_file(vm);
 
     if (vm.count("show-ids")) {
@@ -86,8 +90,9 @@ class RefCheckHandler : public osmium::handler::Handler {
     osmium::index::BoolVector<osmium::unsigned_object_id_type> m_ways;
 
     std::vector<uint32_t> m_relation_ids;
-    std::set<uint32_t> m_member_relation_ids;
-    std::vector<uint32_t> m_missing_relation_ids;
+    std::vector<std::pair<uint32_t, uint32_t>> m_relation_refs;
+
+    osmium::handler::CheckOrder m_check_order;
 
     uint64_t m_node_count = 0;
     uint64_t m_way_count = 0;
@@ -100,7 +105,6 @@ class RefCheckHandler : public osmium::handler::Handler {
     osmium::util::VerboseOutput& m_vout;
     bool m_show_ids;
     bool m_check_relations;
-    bool m_relations_done = false;
 
 public:
 
@@ -134,28 +138,31 @@ public:
         return m_missing_ways_in_relations;
     }
 
-    uint64_t missing_relations_in_relations() {
-        if (!m_relations_done) {
-            std::sort(m_relation_ids.begin(), m_relation_ids.end());
-
-            std::set_difference(m_member_relation_ids.cbegin(), m_member_relation_ids.cend(),
-                                m_relation_ids.cbegin(), m_relation_ids.cend(),
-                                std::back_inserter(m_missing_relation_ids));
+    uint64_t missing_relations_in_relations() const {
+        return m_relation_refs.size();
+    }
 
-            m_relations_done = true;
-        }
+    void find_missing_relations() {
+        std::sort(m_relation_refs.begin(), m_relation_refs.end());
 
-        return m_missing_relation_ids.size();
+        m_relation_refs.erase(
+            std::remove_if(m_relation_refs.begin(), m_relation_refs.end(), [this](std::pair<uint32_t, uint32_t> refs){
+                return std::binary_search(m_relation_ids.begin(), m_relation_ids.end(), refs.first);
+                }),
+                m_relation_refs.end()
+            );
     }
 
-    bool any_errors() {
-        return missing_nodes_in_ways()          > 0 ||
-               missing_nodes_in_relations()     > 0 ||
-               missing_ways_in_relations()      > 0 ||
-               missing_relations_in_relations() > 0;
+    bool no_errors() {
+        return missing_nodes_in_ways()          == 0 &&
+               missing_nodes_in_relations()     == 0 &&
+               missing_ways_in_relations()      == 0 &&
+               missing_relations_in_relations() == 0;
     }
 
     void node(const osmium::Node& node) {
+        m_check_order.node(node);
+
         if (m_node_count == 0) {
             m_vout << "Reading nodes...\n";
         }
@@ -165,6 +172,8 @@ public:
     }
 
     void way(const osmium::Way& way) {
+        m_check_order.way(way);
+
         if (m_way_count == 0) {
             m_vout << "Reading ways...\n";
         }
@@ -185,6 +194,8 @@ public:
     }
 
     void relation(const osmium::Relation& relation) {
+        m_check_order.relation(relation);
+
         if (m_relation_count == 0) {
             m_vout << "Reading relations...\n";
         }
@@ -213,7 +224,9 @@ public:
                         }
                         break;
                     case osmium::item_type::relation:
-                        m_member_relation_ids.insert(uint32_t(relation.id()));
+                        if (member.ref() > relation.id() || !std::binary_search(m_relation_ids.begin(), m_relation_ids.end(), uint32_t(member.ref()))) {
+                            m_relation_refs.emplace_back(uint32_t(member.ref()), uint32_t(relation.id()));
+                        }
                         break;
                     default:
                         break;
@@ -223,8 +236,8 @@ public:
     }
 
     void show_missing_relation_ids() {
-        for (auto id : m_missing_relation_ids) {
-            std::cout << "r" << id << " in r\n";
+        for (const auto& refs : m_relation_refs) {
+            std::cout << "r" << refs.first << " in r" << refs.second << "\n";
         }
     }
 
@@ -234,7 +247,22 @@ bool CommandCheckRefs::run() {
     osmium::io::Reader reader(m_input_file);
 
     RefCheckHandler handler(m_vout, m_show_ids, m_check_relations);
-    osmium::apply(reader, handler);
+
+    try {
+        osmium::apply(reader, handler);
+    } catch (osmium::out_of_order_error& e) {
+        std::cerr << e.what() << "\n";
+        std::cerr << "This command expects the input file to be ordered: First nodes in order of ID,\nthen ways in order of ID, then relations in order of ID.\n";
+        exit(1);
+    }
+
+    if (m_check_relations) {
+        handler.find_missing_relations();
+
+        if (m_show_ids) {
+            handler.show_missing_relation_ids();
+        }
+    }
 
     std::cerr << "There are " << handler.node_count() << " nodes, " << handler.way_count() << " ways, and " << handler.relation_count() << " relations in this file.\n";
 
@@ -247,14 +275,12 @@ bool CommandCheckRefs::run() {
         std::cerr << "Nodes in ways missing: " << handler.missing_nodes_in_ways() << "\n";
     }
 
-    if (m_show_ids) {
-        handler.show_missing_relation_ids();
-    }
-
     reader.close();
+
+    show_memory_used();
     m_vout << "Done.\n";
 
-    return !handler.any_errors();
+    return handler.no_errors();
 }
 
 namespace {
diff --git a/src/command_check_refs.hpp b/src/command_check_refs.hpp
index 8a9d037..db38125 100644
--- a/src/command_check_refs.hpp
+++ b/src/command_check_refs.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -42,6 +42,14 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "check-refs";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium check-refs [OPTIONS] OSM-DATA-FILE";
+    }
+
 }; // class CommandCheckRefs
 
 
diff --git a/src/command_fileinfo.cpp b/src/command_fileinfo.cpp
index ee0e061..d324952 100644
--- a/src/command_fileinfo.cpp
+++ b/src/command_fileinfo.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -484,31 +484,37 @@ public:
 /*************************************************************************/
 
 bool CommandFileinfo::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("extended,e", "Extended output")
     ("get,g", po::value<std::string>(), "Get value")
     ("show-variables,G", "Show variables for --get option")
     ("json,j", "JSON output")
     ;
 
-    add_single_input_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "Input file")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
+    setup_common(vm, desc);
+
     if (vm.count("extended")) {
         m_extended = true;
     }
@@ -602,6 +608,8 @@ bool CommandFileinfo::run() {
     reader.close();
     output->output();
 
+    m_vout << "Done.\n";
+
     return true;
 }
 
diff --git a/src/command_fileinfo.hpp b/src/command_fileinfo.hpp
index 8342c34..6efecbd 100644
--- a/src/command_fileinfo.hpp
+++ b/src/command_fileinfo.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -45,6 +45,14 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "fileinfo";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium fileinfo [OPTIONS] OSM-FILE";
+    }
+
 }; // class CommandFileinfo
 
 
diff --git a/src/command_getid.cpp b/src/command_getid.cpp
index 6536363..2c8b1c6 100644
--- a/src/command_getid.cpp
+++ b/src/command_getid.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -20,6 +20,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
+#include <fstream>
+#include <iterator>
 #include <numeric>
 #include <string>
 #include <vector>
@@ -35,68 +37,146 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "command_getid.hpp"
 #include "exception.hpp"
 
-std::vector<osmium::object_id_type>& CommandGetId::ids(osmium::item_type type) noexcept {
+std::set<osmium::object_id_type>& CommandGetId::ids(osmium::item_type type) noexcept {
     return m_ids[osmium::item_type_to_nwr_index(type)];
 }
 
-const std::vector<osmium::object_id_type>& CommandGetId::ids(osmium::item_type type) const noexcept {
+const std::set<osmium::object_id_type>& CommandGetId::ids(osmium::item_type type) const noexcept {
     return m_ids[osmium::item_type_to_nwr_index(type)];
 }
 
-void CommandGetId::sort_unique(osmium::item_type type) {
-    auto& index = ids(type);
-    std::sort(index.begin(), index.end());
-    auto last = std::unique(index.begin(), index.end());
-    index.erase(last, index.end());
+void CommandGetId::parse_and_add_id(const std::string& s) {
+    auto p = osmium::string_to_object_id(s.c_str(), osmium::osm_entity_bits::nwr, m_default_item_type);
+    ids(p.first).insert(p.second);
+}
+
+void CommandGetId::read_id_file(std::istream& stream) {
+    m_vout << "Reading ID file...\n";
+    for (std::string line; std::getline(stream, line); ) {
+        auto pos = line.find_first_of(" #");
+        if (pos != std::string::npos) {
+            line.erase(pos);
+        }
+        if (!line.empty()) {
+            parse_and_add_id(line);
+        }
+    }
+}
+
+bool CommandGetId::no_ids() const {
+    return ids(osmium::item_type::node).empty() &&
+           ids(osmium::item_type::way).empty() &&
+           ids(osmium::item_type::relation).empty();
+}
+
+size_t CommandGetId::count_ids() const {
+    return ids(osmium::item_type::node).size() +
+           ids(osmium::item_type::way).size() +
+           ids(osmium::item_type::relation).size();
 }
 
 bool CommandGetId::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
+    ("default-type", po::value<std::string>()->default_value("node"), "Default item type")
+    ("id-file,i", po::value<std::string>(), "Read OSM IDs from text file")
+    ("id-osm-file,I", po::value<std::string>(), "Read OSM IDs from OSM file")
+    ("history,H", "Make it work with history files")
+    ("add-referenced,r", "Recursively add referenced objects")
+    ("verbose-ids", "Print all requested and missing IDs")
+    ;
 
-    add_common_options(cmdline);
-    add_single_input_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "OSM input file")
-    ("ids", po::value<std::vector<std::string>>(), "OSM ids")
+    ("ids", po::value<std::vector<std::string>>(), "OSM IDs")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
     positional.add("ids", -1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_file(vm);
     setup_output_file(vm);
 
+    if (vm.count("add-referenced")) {
+        if (m_input_filename == "-") {
+            throw argument_error("Can not read OSM input from STDIN when -r/--add-referenced option is used.");
+        }
+        m_add_referenced_objects = true;
+    }
+
+    if (vm.count("history")) {
+        m_work_with_history = true;
+    }
+
+    if (vm.count("default-type")) {
+        std::string t{vm["default-type"].as<std::string>()};
+
+        if (t == "n" || t == "node") {
+            m_default_item_type = osmium::item_type::node;
+        } else if (t == "w" || t == "way") {
+            m_default_item_type = osmium::item_type::way;
+        } else if (t == "r" || t == "relation") {
+            m_default_item_type = osmium::item_type::relation;
+        } else {
+            throw argument_error(std::string("Unknown default type '") + t + "' (Allowed are 'node', 'way', and 'relation').");
+        }
+    }
+
+    if (vm.count("verbose-ids")) {
+        m_vout.verbose(true);
+        m_verbose_ids = true;
+    }
+
+    if (vm.count("id-file")) {
+        std::string filename = vm["id-file"].as<std::string>();
+
+        if (filename == "-") {
+            if (m_input_filename == "-") {
+                throw argument_error("Can not read OSM input and IDs both from STDIN.");
+            }
+            read_id_file(std::cin);
+        } else {
+            std::ifstream id_file{filename};
+            if (!id_file.is_open()) {
+                throw argument_error("Could not open file '" + filename + "'");
+            }
+            read_id_file(id_file);
+        }
+    }
+
+    if (vm.count("id-osm-file")) {
+        read_id_osm_file(vm["id-osm-file"].as<std::string>());
+    }
+
     if (vm.count("ids")) {
         std::string sids;
         for (const auto& s : vm["ids"].as<std::vector<std::string>>()) {
             sids += s + " ";
         }
-        auto vids = osmium::split_string(sids, "\t ;,/|", true);
-        for (const auto& s : vids) {
-            auto p = osmium::string_to_object_id(s.c_str(), osmium::osm_entity_bits::nwr);
-            if (p.first == osmium::item_type::undefined) {
-                p.first = osmium::item_type::node;
-            }
-            ids(p.first).push_back(p.second);
+        for (const auto& s : osmium::split_string(sids, "\t ;,/|", true)) {
+            parse_and_add_id(s);
         }
+    }
 
-        sort_unique(osmium::item_type::node);
-        sort_unique(osmium::item_type::way);
-        sort_unique(osmium::item_type::relation);
-    } else {
-        throw argument_error("Need at least one id to look for...");
+    if (no_ids()) {
+        throw argument_error("Please specify IDs to look for on command line or with option -i/--id-file or -I/--id-osm-file.");
     }
 
     return true;
@@ -107,22 +187,31 @@ void CommandGetId::show_arguments() {
     show_output_arguments(m_vout);
 
     m_vout << "  other options:\n";
-    m_vout << "    looking for these ids:\n";
-    m_vout << "      nodes:";
-    for (osmium::object_id_type id : ids(osmium::item_type::node)) {
-        m_vout << " " << id;
-    }
-    m_vout << "\n";
-    m_vout << "      ways:";
-    for (osmium::object_id_type id : ids(osmium::item_type::way)) {
-        m_vout << " " << id;
-    }
-    m_vout << "\n";
-    m_vout << "      relations:";
-    for (osmium::object_id_type id : ids(osmium::item_type::relation)) {
-        m_vout << " " << id;
+    m_vout << "    add referenced objects: " << (m_add_referenced_objects ? "yes" : "no") << "\n";
+    m_vout << "    work with history files: " << (m_work_with_history ? "yes" : "no") << "\n";
+    m_vout << "    default object type: " << osmium::item_type_to_name(m_default_item_type) << "\n";
+    if (m_verbose_ids) {
+        m_vout << "    looking for these ids:\n";
+        m_vout << "      nodes:";
+        for (osmium::object_id_type id : ids(osmium::item_type::node)) {
+            m_vout << " " << id;
+        }
+        m_vout << "\n";
+        m_vout << "      ways:";
+        for (osmium::object_id_type id : ids(osmium::item_type::way)) {
+            m_vout << " " << id;
+        }
+        m_vout << "\n";
+        m_vout << "      relations:";
+        for (osmium::object_id_type id : ids(osmium::item_type::relation)) {
+            m_vout << " " << id;
+        }
+        m_vout << "\n";
+    } else {
+        m_vout << "    looking for " << ids(osmium::item_type::node).size() << " node ID(s), "
+                                     << ids(osmium::item_type::way).size() << " way ID(s), and "
+                                     << ids(osmium::item_type::relation).size() << " relation ID(s)\n";
     }
-    m_vout << "\n";
 }
 
 osmium::osm_entity_bits::type CommandGetId::get_needed_types() const {
@@ -141,51 +230,189 @@ osmium::osm_entity_bits::type CommandGetId::get_needed_types() const {
     return types;
 }
 
+void CommandGetId::add_nodes(const osmium::Way& way) {
+    for (const auto& nr : way.nodes()) {
+        ids(osmium::item_type::node).insert(nr.ref());
+    }
+}
+
+void CommandGetId::add_members(const osmium::Relation& relation) {
+    for (const auto& member : relation.members()) {
+        ids(member.type()).insert(member.ref());
+    }
+}
+
+static void print_missing_ids(const char* type, std::set<osmium::object_id_type>& set) {
+    if (set.empty()) {
+        return;
+    }
+    std::cerr << "Missing " << type << " IDs:";
+    for (const auto& id : set) {
+        std::cerr << ' ' << id;
+    }
+    std::cerr << '\n';
+}
+
+void CommandGetId::read_id_osm_file(const std::string& file_name) {
+    m_vout << "Reading OSM ID file...\n";
+    osmium::io::Reader reader(file_name, osmium::osm_entity_bits::object);
+    while (osmium::memory::Buffer buffer = reader.read()) {
+        for (const auto& object : buffer.select<osmium::OSMObject>()) {
+            ids(object.type()).insert(object.id());
+            if (object.type() == osmium::item_type::way) {
+                add_nodes(static_cast<const osmium::Way&>(object));
+            } else if (object.type() == osmium::item_type::relation) {
+                add_members(static_cast<const osmium::Relation&>(object));
+            }
+        }
+    }
+    reader.close();
+}
+
+void CommandGetId::mark_rel_ids(const std::multimap<osmium::object_id_type, osmium::object_id_type>& rel_in_rel, osmium::object_id_type id) {
+    auto range = rel_in_rel.equal_range(id);
+    for (auto it = range.first; it != range.second; ++it) {
+        if (ids(osmium::item_type::relation).count(it->second) == 0) {
+            ids(osmium::item_type::relation).insert(it->second);
+            mark_rel_ids(rel_in_rel, it->second);
+        }
+    }
+}
+
+bool CommandGetId::find_relations_in_relations() {
+    m_vout << "  Reading input file to find relations in relations...\n";
+    std::multimap<osmium::object_id_type, osmium::object_id_type> rel_in_rel;
+
+    osmium::io::Reader reader(m_input_file, osmium::osm_entity_bits::relation);
+    while (osmium::memory::Buffer buffer = reader.read()) {
+        for (const auto& relation : buffer.select<osmium::Relation>()) {
+            for (const auto& member : relation.members()) {
+                if (member.type() == osmium::item_type::relation) {
+                    rel_in_rel.emplace(relation.id(), member.ref());
+                } else if (ids(osmium::item_type::relation).count(relation.id())) {
+                    if (member.type() == osmium::item_type::node) {
+                        ids(osmium::item_type::node).insert(member.ref());
+                    } else if (member.type() == osmium::item_type::way) {
+                        ids(osmium::item_type::way).insert(member.ref());
+                    }
+                }
+            }
+        }
+    }
+    reader.close();
+
+    if (rel_in_rel.empty()) {
+        return false;
+    }
+
+    for (const osmium::object_id_type id : ids(osmium::item_type::relation)) {
+        mark_rel_ids(rel_in_rel, id);
+    }
+
+    return true;
+}
+
+void CommandGetId::find_nodes_and_ways_in_relations() {
+    m_vout << "  Reading input file to find nodes/ways in relations...\n";
+
+    osmium::io::Reader reader(m_input_file, osmium::osm_entity_bits::relation);
+    while (osmium::memory::Buffer buffer = reader.read()) {
+        for (const auto& relation : buffer.select<osmium::Relation>()) {
+            if (ids(osmium::item_type::relation).count(relation.id())) {
+                for (const auto& member : relation.members()) {
+                    if (member.type() == osmium::item_type::node) {
+                        ids(osmium::item_type::node).insert(member.ref());
+                    } else if (member.type() == osmium::item_type::way) {
+                        ids(osmium::item_type::way).insert(member.ref());
+                    }
+                }
+            }
+        }
+    }
+    reader.close();
+}
+
+void CommandGetId::find_nodes_in_ways() {
+    m_vout << "  Reading input file to find nodes in ways...\n";
+
+    osmium::io::Reader reader(m_input_file, osmium::osm_entity_bits::way);
+    while (osmium::memory::Buffer buffer = reader.read()) {
+        for (const auto& way : buffer.select<osmium::Way>()) {
+            if (ids(osmium::item_type::way).count(way.id())) {
+                add_nodes(way);
+            }
+        }
+    }
+    reader.close();
+}
+
+void CommandGetId::find_referenced_objects() {
+    m_vout << "Following references...\n";
+    bool todo = !ids(osmium::item_type::relation).empty();
+    if (todo) {
+        todo = find_relations_in_relations();
+    }
+
+    if (todo) {
+        find_nodes_and_ways_in_relations();
+    }
+
+    if (!ids(osmium::item_type::way).empty()) {
+        find_nodes_in_ways();
+    }
+    m_vout << "Done following references.\n";
+}
+
 bool CommandGetId::run() {
+    if (m_add_referenced_objects) {
+        find_referenced_objects();
+    }
+
     m_vout << "Opening input file...\n";
     osmium::io::Reader reader(m_input_file, get_needed_types());
 
     m_vout << "Opening output file...\n";
-    osmium::io::Header header;
+    osmium::io::Header header = reader.header();
     header.set("generator", m_generator);
     osmium::io::Writer writer(m_output_file, header, m_output_overwrite, m_fsync);
 
-    m_vout << "Reading input file...\n";
-    auto input = osmium::io::make_input_iterator_range<osmium::OSMObject>(reader);
-
-    osmium::memory::Buffer output_buffer(10240);
-
-    size_t num_ids = ids(osmium::item_type::node).size() +
-                     ids(osmium::item_type::way).size() +
-                     ids(osmium::item_type::relation).size();
-
-    for (const osmium::OSMObject& object : input) {
-        const auto& index = ids(object.type());
-        if (std::binary_search(index.begin(), index.end(), object.id())) {
-            output_buffer.add_item(object);
-            output_buffer.commit();
-            --num_ids;
+    m_vout << "Copying from source to output file...\n";
+    while (osmium::memory::Buffer buffer = reader.read()) {
+        for (const auto& object : buffer.select<osmium::OSMObject>()) {
+            auto& index = ids(object.type());
+            if (index.count(object.id())) {
+                if (!m_work_with_history) {
+                    index.erase(object.id());
+                }
+                writer(object);
+            }
         }
     }
 
-    m_vout << "Writing out results...\n";
-    writer(std::move(output_buffer));
-
     m_vout << "Closing output file...\n";
     writer.close();
 
-    if (num_ids == 0) {
-        m_vout << "Found all objects.\n";
-    } else {
-        m_vout << "Did not find " << num_ids << " objects.\n";
-    }
-
     m_vout << "Closing input file...\n";
     reader.close();
 
+    if (!m_work_with_history) {
+        if (no_ids()) {
+            m_vout << "Found all objects.\n";
+        } else {
+            m_vout << "Did not find " << count_ids() << " object(s).\n";
+            if (m_verbose_ids) {
+                print_missing_ids("node",     ids(osmium::item_type::node));
+                print_missing_ids("way",      ids(osmium::item_type::way));
+                print_missing_ids("relation", ids(osmium::item_type::relation));
+            }
+        }
+    }
+
+    show_memory_used();
+
     m_vout << "Done.\n";
 
-    return num_ids == 0;
+    return m_work_with_history || no_ids();
 }
 
 namespace {
diff --git a/src/command_getid.hpp b/src/command_getid.hpp
index c5a74f2..a69da79 100644
--- a/src/command_getid.hpp
+++ b/src/command_getid.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,9 +23,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
+#include <map>
+#include <set>
 #include <string>
 #include <vector>
 
+#include <osmium/fwd.hpp>
 #include <osmium/osm/entity_bits.hpp>
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/types.hpp>
@@ -34,25 +37,56 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 class CommandGetId : public Command, public with_single_osm_input, public with_osm_output {
 
-    std::vector<osmium::object_id_type> m_ids[3];
+    osmium::item_type m_default_item_type = osmium::item_type::node;
 
-public:
+    bool m_add_referenced_objects = false;
+    bool m_work_with_history = false;
+    bool m_verbose_ids = false;
 
-    CommandGetId() = default;
+    std::set<osmium::object_id_type> m_ids[3];
+
+    std::set<osmium::object_id_type>& ids(osmium::item_type type) noexcept;
+    const std::set<osmium::object_id_type>& ids(osmium::item_type type) const noexcept;
+
+    void parse_and_add_id(const std::string& s);
+
+    void read_id_osm_file(const std::string& file_name);
+    void read_id_file(std::istream& stream);
+
+    osmium::osm_entity_bits::type get_needed_types() const;
+    bool no_ids() const;
+    size_t count_ids() const;
+
+    void find_referenced_objects();
 
-    std::vector<osmium::object_id_type>& ids(osmium::item_type type) noexcept;
-    const std::vector<osmium::object_id_type>& ids(osmium::item_type type) const noexcept;
+    void add_nodes(const osmium::Way& way);
+    void add_members(const osmium::Relation& relation);
 
-    void sort_unique(osmium::item_type type);
+    void mark_rel_ids(const std::multimap<osmium::object_id_type, osmium::object_id_type>& rel_in_rel, osmium::object_id_type id);
+    bool find_relations_in_relations();
+    void find_nodes_and_ways_in_relations();
+    void find_nodes_in_ways();
+
+public:
+
+    CommandGetId() = default;
 
     bool setup(const std::vector<std::string>& arguments) override final;
 
     void show_arguments() override final;
 
-    osmium::osm_entity_bits::type get_needed_types() const;
-
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "getid";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium getid [OPTIONS] OSM-FILE ID...\n"
+               "       osmium getid [OPTIONS] OSM-FILE -i ID-FILE\n"
+               "       osmium getid [OPTIONS] OSM-FILE -I ID-OSM-FILE";
+    }
+
 }; // class CommandGetId
 
 
diff --git a/src/command_help.cpp b/src/command_help.cpp
index b97d6ce..a6598ea 100644
--- a/src/command_help.cpp
+++ b/src/command_help.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -56,8 +56,8 @@ bool CommandHelp::run() {
     auto commands = CommandFactory::help();
 
     if (m_topic == "help") {
-        std::cout << "Usage: osmium COMMAND [ARG...]\n";
-        std::cout << "       osmium --version\n\nCommands are:\n";
+        std::cout << "Usage: " << synopsis()
+                  << "\n\nCOMMANDS:\n";
 
         // print command names and descriptions in a nice table
         for (const auto& cmd : commands) {
@@ -71,7 +71,8 @@ bool CommandHelp::run() {
                       << "\n";
         }
 
-        std::cout << "\nSee 'osmium help COMMAND' for more information on a specific command." << std::endl;
+        std::cout << "\nUse 'osmium COMMAND -h' for short usage information.\n"
+                     "Use 'osmium help COMMAND' for detailed information on a specific command.\n";
         return true;
     }
 
diff --git a/src/command_help.hpp b/src/command_help.hpp
index 794e6c9..79aa962 100644
--- a/src/command_help.hpp
+++ b/src/command_help.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -35,6 +35,15 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "help";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium COMMAND [ARG...]\n"
+               "       osmium --version";
+    }
+
 }; // class CommandHelp
 
 
diff --git a/src/command_merge_changes.cpp b/src/command_merge_changes.cpp
index 43c2bbe..c1fab42 100644
--- a/src/command_merge_changes.cpp
+++ b/src/command_merge_changes.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -33,31 +33,34 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "command_merge_changes.hpp"
 
 bool CommandMergeChanges::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("simplify,s", "Simplify change")
     ;
 
-    add_common_options(cmdline);
-    add_multiple_inputs_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_multiple_inputs_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filenames", po::value<std::vector<std::string>>(), "Input files")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filenames", -1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_files(vm);
     setup_output_file(vm);
 
@@ -119,6 +122,7 @@ bool CommandMergeChanges::run() {
     m_vout << "Closing output file...\n";
     writer.close();
 
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_merge_changes.hpp b/src/command_merge_changes.hpp
index 0a4a5ad..66e1d9f 100644
--- a/src/command_merge_changes.hpp
+++ b/src/command_merge_changes.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -42,6 +42,14 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "merge-changes";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium merge-changes [OPTIONS] OSM-CHANGE-FILE...";
+    }
+
 }; // class CommandMergeChanges
 
 
diff --git a/src/command_renumber.cpp b/src/command_renumber.cpp
index fdc8fd8..805c9e3 100644
--- a/src/command_renumber.cpp
+++ b/src/command_renumber.cpp
@@ -2,7 +2,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,10 +23,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <fcntl.h>
 #include <iostream>
 #include <iterator>
-#include <map>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <vector>
 
 #ifdef _WIN32
 # include <io.h>
@@ -38,37 +36,95 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <osmium/io/any_input.hpp>
 #include <osmium/io/any_output.hpp>
 #include <osmium/io/detail/read_write.hpp>
+#include <osmium/osm/entity_bits.hpp>
 #include <osmium/util/file.hpp>
 #include <osmium/util/memory_mapping.hpp>
 
 #include "command_renumber.hpp"
 
+osmium::object_id_type id_map::operator()(osmium::object_id_type id) {
+    // Search for id in m_extra_ids and return if found.
+    auto it = m_extra_ids.find(id);
+    if (it != m_extra_ids.end()) {
+        return it->second;
+    }
+
+    // New ID is larger than all existing IDs. Add it to end and return.
+    if (m_ids.empty() || id > m_ids.back()) {
+        m_ids.push_back(id);
+        return m_ids.size();
+    }
+
+    const auto element = std::lower_bound(m_ids.cbegin(), m_ids.cend(), id);
+    // Old ID not found in m_ids, add to m_extra_ids.
+    if (element == m_ids.cend() || *element != id) {
+        m_ids.push_back(m_ids.back());
+        m_extra_ids[id] = m_ids.size();
+        return m_ids.size();
+    }
+
+    // Old ID found in m_ids, return.
+    return osmium::object_id_type(std::distance(m_ids.cbegin(), element) + 1);
+}
+
+void id_map::write(int fd) {
+    for (const auto& m : m_extra_ids) {
+        m_ids[m.second - 1] = m.first;
+    }
+
+    osmium::io::detail::reliable_write(
+        fd,
+        reinterpret_cast<const char*>(m_ids.data()),
+        sizeof(osmium::object_id_type) * m_ids.size()
+    );
+}
+
+void id_map::read(int fd, size_t file_size) {
+    auto num_elements = file_size / sizeof(osmium::object_id_type);
+    m_ids.reserve(num_elements);
+    osmium::util::TypedMemoryMapping<osmium::object_id_type> mapping{num_elements, osmium::util::MemoryMapping::mapping_mode::readonly, fd};
+
+    osmium::object_id_type last_id = 0;
+    for (osmium::object_id_type id : mapping) {
+        if (id > last_id) {
+            m_ids.push_back(id);
+            last_id = id;
+        } else {
+            m_ids.push_back(last_id);
+            m_extra_ids[id] = m_ids.size();
+        }
+    }
+}
+
 bool CommandRenumber::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
     ("index-directory,i", po::value<std::string>(), "Index directory")
     ;
 
-    add_common_options(cmdline);
-    add_single_input_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "Input file")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_file(vm);
     setup_output_file(vm);
 
@@ -87,31 +143,25 @@ void CommandRenumber::show_arguments() {
     m_vout << "    index directory: " << m_index_directory << "\n";
 }
 
-osmium::object_id_type CommandRenumber::lookup(osmium::item_type type, osmium::object_id_type id) {
-    try {
-        return index(type).at(id);
-    } catch (std::out_of_range&) {
-        index(type)[id] = ++last_id(type);
-        return last_id(type);
-    }
-}
-
 void CommandRenumber::renumber(osmium::memory::Buffer& buffer) {
-    for (auto it = buffer.begin<osmium::OSMObject>(); it != buffer.end<osmium::OSMObject>(); ++it) {
-        switch (it->type()) {
+    for (auto& object : buffer.select<osmium::OSMObject>()) {
+        switch (object.type()) {
             case osmium::item_type::node:
-                it->set_id(lookup(osmium::item_type::node, it->id()));
+                m_check_order.node(static_cast<const osmium::Node&>(object));
+                object.set_id(map(osmium::item_type::node)(object.id()));
                 break;
             case osmium::item_type::way:
-                it->set_id(lookup(osmium::item_type::way, it->id()));
-                for (auto& ref : static_cast<osmium::Way&>(*it).nodes()) {
-                    ref.set_ref(lookup(osmium::item_type::node, ref.ref()));
+                m_check_order.way(static_cast<const osmium::Way&>(object));
+                object.set_id(map(osmium::item_type::way)(object.id()));
+                for (auto& ref : static_cast<osmium::Way&>(object).nodes()) {
+                    ref.set_ref(map(osmium::item_type::node)(ref.ref()));
                 }
                 break;
             case osmium::item_type::relation:
-                it->set_id(lookup(osmium::item_type::relation, it->id()));
-                for (auto& member : static_cast<osmium::Relation&>(*it).members()) {
-                    member.set_ref(lookup(member.type(), member.ref()));
+                m_check_order.relation(static_cast<const osmium::Relation&>(object));
+                object.set_id(map(osmium::item_type::relation)(object.id()));
+                for (auto& member : static_cast<osmium::Relation&>(object).members()) {
+                    member.set_ref(map(member.type())(member.ref()));
                 }
                 break;
             default:
@@ -120,20 +170,12 @@ void CommandRenumber::renumber(osmium::memory::Buffer& buffer) {
     }
 }
 
-std::string CommandRenumber::filename(const std::string& name) {
+std::string CommandRenumber::filename(const char* name) const {
     return m_index_directory + "/" + name + ".idx";
 }
 
-remap_index_type& CommandRenumber::index(osmium::item_type type) {
-    return m_id_index[osmium::item_type_to_nwr_index(type)];
-}
-
-osmium::object_id_type& CommandRenumber::last_id(osmium::item_type type) {
-    return m_last_id[osmium::item_type_to_nwr_index(type)];
-}
-
-void CommandRenumber::read_index(osmium::item_type type, const std::string& name) {
-    std::string f { filename(name) };
+void CommandRenumber::read_index(osmium::item_type type) {
+    std::string f { filename(osmium::item_type_to_name(type)) };
     int fd = ::open(f.c_str(), O_RDONLY);
     if (fd < 0) {
         // if the file is not there we don't have to read anything and can return
@@ -147,28 +189,18 @@ void CommandRenumber::read_index(osmium::item_type type, const std::string& name
 #endif
 
     size_t file_size = osmium::util::file_size(fd);
-    if (file_size % sizeof(remap_index_value_type) != 0) {
+
+    if (file_size % sizeof(osmium::object_id_type) != 0) {
         throw std::runtime_error(std::string("index file '") + f + "' has wrong file size");
     }
 
-    {
-        osmium::util::TypedMemoryMapping<remap_index_value_type> mapping(file_size / sizeof(remap_index_value_type), osmium::util::MemoryMapping::mapping_mode::readonly, fd);
-        std::copy(mapping.begin(), mapping.end(), std::inserter(index(type), index(type).begin()));
-
-        last_id(type) = std::max_element(mapping.begin(),
-                                         mapping.end(),
-                                         [](const remap_index_value_type& a,
-                                            const remap_index_value_type& b) {
-                                             return a.second < b.second;
-                                         }
-                        )->second;
-    }
+    map(type).read(fd, file_size);
 
     close(fd);
 }
 
-void CommandRenumber::write_index(osmium::item_type type, const std::string& name) {
-    std::string f { filename(name) };
+void CommandRenumber::write_index(osmium::item_type type) {
+    std::string f { filename(osmium::item_type_to_name(type)) };
     int fd = ::open(f.c_str(), O_WRONLY | O_CREAT, 0666);
     if (fd < 0) {
         throw std::runtime_error(std::string("Can't open file '") + f + "': " + strerror(errno));
@@ -177,30 +209,41 @@ void CommandRenumber::write_index(osmium::item_type type, const std::string& nam
     _setmode(fd, _O_BINARY);
 #endif
 
-    std::vector<remap_index_value_type> data;
-    std::copy(index(type).begin(), index(type).end(), std::back_inserter(data));
-    osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(data.data()), sizeof(remap_index_value_type) * data.size());
+    map(type).write(fd);
 
     close(fd);
 }
 
+void read_relations(const osmium::io::File& input_file, id_map& map) {
+    osmium::io::Reader reader_pass1(input_file, osmium::osm_entity_bits::relation);
+
+    auto input = osmium::io::make_input_iterator_range<osmium::Relation>(reader_pass1);
+    for (const osmium::Relation& relation : input) {
+        map(relation.id());
+    }
+
+    reader_pass1.close();
+}
+
 bool CommandRenumber::run() {
     if (!m_index_directory.empty()) {
         m_vout << "Reading index files...\n";
-        read_index(osmium::item_type::node, "nodes");
-        read_index(osmium::item_type::way, "ways");
-        read_index(osmium::item_type::relation, "relations");
+        read_index(osmium::item_type::node);
+        read_index(osmium::item_type::way);
+        read_index(osmium::item_type::relation);
 
-        m_vout << "  Nodes     index contains " << index(osmium::item_type::node).size()     << " items\n";
-        m_vout << "  Ways      index contains " << index(osmium::item_type::way).size()      << " items\n";
-        m_vout << "  Relations index contains " << index(osmium::item_type::relation).size() << " items\n";
+        m_vout << "  Nodes     index contains " << map(osmium::item_type::node).size() << " items\n";
+        m_vout << "  Ways      index contains " << map(osmium::item_type::way).size() << " items\n";
+        m_vout << "  Relations index contains " << map(osmium::item_type::relation).size() << " items\n";
     }
 
     m_vout << "First pass through input file (reading relations)...\n";
-    osmium::io::Reader reader_pass1(m_input_file, osmium::osm_entity_bits::relation);
+    read_relations(m_input_file, map(osmium::item_type::relation));
+
+    m_vout << "Second pass through input file...\n";
+    osmium::io::Reader reader_pass2(m_input_file);
 
-    m_vout << "Opening output file...\n";
-    osmium::io::Header header = reader_pass1.header();
+    osmium::io::Header header = reader_pass2.header();
     header.set("generator", m_generator);
     header.set("xml_josm_upload", "false");
     for (const auto& h : m_output_headers) {
@@ -208,18 +251,16 @@ bool CommandRenumber::run() {
     }
     osmium::io::Writer writer(m_output_file, header, m_output_overwrite, m_fsync);
 
-    auto input = osmium::io::make_input_iterator_range<osmium::Relation>(reader_pass1);
-    for (const osmium::Relation& relation : input) {
-        lookup(osmium::item_type::relation, relation.id());
-    }
-
-    reader_pass1.close();
-
-    m_vout << "Second pass through input file...\n";
-    osmium::io::Reader reader_pass2(m_input_file);
-    while (osmium::memory::Buffer buffer = reader_pass2.read()) {
-        renumber(buffer);
-        writer(std::move(buffer));
+    try {
+        while (osmium::memory::Buffer buffer = reader_pass2.read()) {
+            renumber(buffer);
+            writer(std::move(buffer));
+        }
+    } catch (osmium::out_of_order_error& e) {
+        std::cerr << e.what() << "\n";
+        std::cerr << "This command expects the input file to be ordered: First nodes in order of ID,\n"
+                  << "then ways in order of ID, then relations in order of ID.\n";
+        exit(1);
     }
     reader_pass2.close();
 
@@ -228,11 +269,16 @@ bool CommandRenumber::run() {
 
     if (!m_index_directory.empty()) {
         m_vout << "Writing index files...\n";
-        write_index(osmium::item_type::node, "nodes");
-        write_index(osmium::item_type::way, "ways");
-        write_index(osmium::item_type::relation, "relations");
+        write_index(osmium::item_type::node);
+        write_index(osmium::item_type::way);
+        write_index(osmium::item_type::relation);
     }
 
+    m_vout << "Largest (referenced) node id: "     << map(osmium::item_type::node).size()     << "\n";
+    m_vout << "Largest (referenced) way id: "      << map(osmium::item_type::way).size()      << "\n";
+    m_vout << "Largest (referenced) relation id: " << map(osmium::item_type::relation).size() << "\n";
+
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_renumber.hpp b/src/command_renumber.hpp
index ecd5129..fe77b24 100644
--- a/src/command_renumber.hpp
+++ b/src/command_renumber.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,54 +23,106 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
-#include <map>
+#include <algorithm>
+#include <iterator>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
+#include <osmium/handler/check_order.hpp>
 #include <osmium/memory/buffer.hpp>
-#include <osmium/osm/entity_bits.hpp>
 #include <osmium/osm/types.hpp>
 
 #include "cmd.hpp"
 
-typedef std::map<osmium::unsigned_object_id_type, osmium::unsigned_object_id_type> remap_index_type;
-typedef remap_index_type::value_type remap_index_value_type;
+/**
+ * Holds the mapping from old IDs to new IDs of one object type.
+ */
+class id_map {
+
+    // Internally this uses two different means of storing the mapping:
+    //
+    // Most of the old IDs are stored in a sorted vector. The index into the
+    // vector is the new ID. All IDs from the nodes, ways, and relations
+    // themselves will end up here.
+    std::vector<osmium::object_id_type> m_ids;
+
+    // For IDs that can't be written into the sorted vector because this would
+    // destroy the sorting, a hash map is used. These are the IDs not read
+    // in order, ie the node IDs referenced from the ways and the member IDs
+    // referenced from the relations.
+    std::unordered_map<osmium::object_id_type, osmium::object_id_type> m_extra_ids;
+
+    // Because we still have to allocate unique new IDs for the mappings
+    // ending up in m_extra_ids, we add dummy IDs of the same value as the
+    // last one to the end of the m_ids vector. This gives us new IDs without
+    // destroying the ordering of m_ids. But to find a new ID from an old ID
+    // in m_ids we have to take the first of potentially several identical
+    // IDs we find (using std::lower_bound), its position is then the new ID.
+
+public:
+
+    id_map() = default;
+
+    // Map from old ID to new ID. If the old ID has been seen before, it will
+    // be returned, otherwise a new ID will be allocated and stored.
+    osmium::object_id_type operator()(osmium::object_id_type id);
+
+    // Write the mappings into a file in binary form. This will first copy
+    // the mappings from m_extra_ids into the m_ids vector. After this
+    // operation this object becomes unusable!
+    void write(int fd);
+
+    // Read the mappings from a binary file into m_ids and m_extra_ids.
+    void read(int fd, size_t file_size);
+
+    // The number of mappings currently existing. Also the last allocated
+    // new ID.
+    size_t size() const noexcept {
+        return m_ids.size();
+    }
+
+}; // class id_map
 
 class CommandRenumber : public Command, public with_single_osm_input, public with_osm_output {
 
     std::string m_index_directory;
 
-    remap_index_type m_id_index[3];
-    osmium::object_id_type m_last_id[3];
+    osmium::handler::CheckOrder m_check_order;
 
-public:
+    // id mappings for nodes, ways, and relations
+    id_map m_id_map[3];
 
-    CommandRenumber() {
-        // workaround for MSVC
-        m_last_id[0] = 0;
-        m_last_id[1] = 0;
-        m_last_id[2] = 0;
+    id_map& map(osmium::item_type type) noexcept {
+        return m_id_map[osmium::item_type_to_nwr_index(type)];
     }
 
-    bool setup(const std::vector<std::string>& arguments) override final;
+    void renumber(osmium::memory::Buffer& buffer);
 
-    void show_arguments() override final;
+    std::string filename(const char* name) const;
 
-    osmium::object_id_type lookup(osmium::item_type type, osmium::object_id_type id);
+    void read_index(osmium::item_type type);
 
-    void renumber(osmium::memory::Buffer& buffer);
+    void write_index(osmium::item_type type);
 
-    std::string filename(const std::string& name);
+public:
 
-    remap_index_type& index(osmium::item_type type);
-    osmium::object_id_type& last_id(osmium::item_type type);
+    CommandRenumber() = default;
 
-    void read_index(osmium::item_type type, const std::string& name);
+    bool setup(const std::vector<std::string>& arguments) override final;
 
-    void write_index(osmium::item_type type, const std::string& name);
+    void show_arguments() override final;
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "renumber";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium renumber [OPTIONS] OSM-FILE";
+    }
+
 }; // class CommandRenumber
 
 
diff --git a/src/command_show.cpp b/src/command_show.cpp
new file mode 100644
index 0000000..836a0a9
--- /dev/null
+++ b/src/command_show.cpp
@@ -0,0 +1,225 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium
+
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <iostream>
+#include <iterator>
+
+#ifndef _MSC_VER
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include <boost/program_options.hpp>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/io/any_output.hpp>
+
+#include "command_show.hpp"
+#include "exception.hpp"
+
+#ifndef _MSC_VER
+void CommandShow::setup_pager_from_env() noexcept {
+    m_pager = "less";
+    const char* pager = ::getenv("OSMIUM_PAGER");
+    if (pager) {
+        m_pager = pager;
+    } else {
+        pager = ::getenv("PAGER");
+        if (pager) {
+            m_pager = pager;
+        }
+    }
+
+    if (m_pager == "cat") {
+        m_pager = "";
+    }
+}
+#endif
+
+bool CommandShow::setup(const std::vector<std::string>& arguments) {
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
+    ("format-debug,d", "Use debug format")
+    ("format-opl,o", "Use OPL format")
+    ("format-xml,x", "Use XML format")
+#ifndef _MSC_VER
+    ("no-pager", "Do not run pager program")
+#endif
+    ("object-type,t", po::value<std::vector<std::string>>(), "Read only objects of given type (node, way, relation, changeset)")
+    ("output-format,f", po::value<std::string>(), "Format of output file")
+    ;
+
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+
+    po::options_description hidden;
+    hidden.add_options()
+    ("input-filename", po::value<std::string>(), "Input file")
+    ;
+
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
+
+    po::positional_options_description positional;
+    positional.add("input-filename", 1);
+
+    po::variables_map vm;
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
+    po::notify(vm);
+
+    setup_common(vm, desc);
+    setup_input_file(vm);
+
+#ifndef _MSC_VER
+    if (vm.count("no-pager")) {
+        m_pager = "";
+    } else {
+        setup_pager_from_env();
+    }
+#endif
+
+    if (vm.count("output-format") &&
+        vm.count("format-debug") &&
+        vm.count("format-opl") &&
+        vm.count("format-xml")) {
+        throw argument_error("You can only use at most one of the following options: -f/--output-format, -d/--format-debug, -o/--format-opl, and -x/--format-xml.");
+    }
+
+    if (vm.count("output-format")) {
+        m_output_format = vm["output-format"].as<std::string>();
+    } else if (vm.count("format-debug")) {
+        m_output_format = "debug,color=true";
+    } else if (vm.count("format-opl")) {
+        m_output_format = "opl";
+    } else if (vm.count("format-xml")) {
+        m_output_format = "xml";
+    } else {
+        const char* output_format_from_env = ::getenv("OSMIUM_SHOW_FORMAT");
+        if (output_format_from_env) {
+            m_output_format = output_format_from_env;
+        }
+    }
+
+    return true;
+}
+
+#ifndef _MSC_VER
+static int execute_pager(const std::string& pager) {
+    int pipefd[2];
+    if (::pipe(pipefd) < 0) {
+        throw std::system_error(errno, std::system_category(), "opening pipe failed");
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {
+        throw std::system_error(errno, std::system_category(), "fork failed");
+    }
+
+    if (pid == 0) {
+        // child
+        ::close(pipefd[1]); // close write end of the pipe
+        ::close(0); // close stdin
+        if (::dup2(pipefd[0], 0) < 0) { // put end of pipe as stdin
+            exit(1);
+        }
+
+        // execute pager without arguments
+        ::execlp(pager.c_str(), pager.c_str(), nullptr);
+
+        // Exec will either succeed and never return here, or it fails and
+        // we'll exit.
+        exit(1);
+    }
+
+    // parent
+    ::close(pipefd[0]); // close read end of the pipe
+
+    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+        throw std::system_error(errno, std::system_category(), "signal call failed");
+    }
+
+    return pipefd[1];
+}
+#endif
+
+bool CommandShow::run() {
+    osmium::io::Reader reader(m_input_file, osm_entity_bits());
+    osmium::io::Header header = reader.header();
+
+    if (m_pager.empty()) {
+        osmium::io::File file("-", m_output_format);
+        osmium::io::Writer writer(file, header);
+        while (osmium::memory::Buffer buffer = reader.read()) {
+            writer(std::move(buffer));
+        }
+        writer.close();
+    } else {
+#ifndef _MSC_VER
+        int fd = execute_pager(m_pager);
+
+        ::close(1); // close stdout
+        if (::dup2(fd, 1) < 0) { // put end of pipe as stdout
+            throw std::system_error(errno, std::system_category(), "dup2 failed");
+        }
+
+        osmium::io::File file("-", m_output_format);
+        osmium::io::Writer writer(file, header);
+        try {
+            while (osmium::memory::Buffer buffer = reader.read()) {
+                writer(std::move(buffer));
+            }
+        } catch (std::system_error& e) {
+            if (e.code().value() != EPIPE) {
+                throw;
+            }
+        }
+
+        close(fd);
+        writer.close();
+
+        int status = 0;
+        int pid = ::wait(&status);
+        if (pid < 0) {
+            throw std::system_error(errno, std::system_category(), "wait failed");
+        }
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 1) {
+            throw argument_error(std::string{"Could not execute pager '"} + m_pager + "'");
+        }
+#endif
+    }
+
+    reader.close();
+
+    return true;
+}
+
+namespace {
+
+    const bool register_show_command = CommandFactory::add("show", "Show OSM file contents", []() {
+        return new CommandShow();
+    });
+
+}
+
diff --git a/src/command_cat.hpp b/src/command_show.hpp
similarity index 61%
copy from src/command_cat.hpp
copy to src/command_show.hpp
index 508879b..2b7fdd7 100644
--- a/src/command_cat.hpp
+++ b/src/command_show.hpp
@@ -1,12 +1,12 @@
-#ifndef COMMAND_CAT_HPP
-#define COMMAND_CAT_HPP
+#ifndef COMMAND_SHOW_HPP
+#define COMMAND_SHOW_HPP
 
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -27,31 +27,33 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <vector>
 
 #include <osmium/io/header.hpp>
-#include <osmium/osm/entity_bits.hpp>
 
 #include "cmd.hpp"
 
-class CommandCat : public Command, public with_multiple_osm_inputs, public with_osm_output {
+class CommandShow : public Command, public with_single_osm_input {
 
-    osmium::osm_entity_bits::type m_osm_entity_bits = osmium::osm_entity_bits::all;
+    std::string m_output_format{"debug,color=true"};
+    std::string m_pager;
+
+    void setup_pager_from_env() noexcept;
 
 public:
 
-    CommandCat() = default;
+    CommandShow() = default;
 
     bool setup(const std::vector<std::string>& arguments) override final;
 
-    void show_arguments() override final;
-
     bool run() override final;
 
-    void setup_header(osmium::io::Header& header) const;
+    const char* name() const noexcept override final {
+        return "show";
+    }
 
-    osmium::osm_entity_bits::type osm_entity_bits() const {
-        return m_osm_entity_bits;
+    const char* synopsis() const noexcept override final {
+        return "osmium show [OPTIONS] OSM-FILE";
     }
 
-}; // class CommandCat
+}; // class CommandShow
 
 
-#endif // COMMAND_CAT_HPP
+#endif // COMMAND_SHOW_HPP
diff --git a/src/command_sort.cpp b/src/command_sort.cpp
index 2e1454a..3c49a17 100644
--- a/src/command_sort.cpp
+++ b/src/command_sort.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -32,30 +32,29 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "command_sort.hpp"
 
 bool CommandSort::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
-    cmdline.add_options()
-    ;
-
-    add_common_options(cmdline);
-    add_multiple_inputs_options(cmdline);
-    add_output_options(cmdline);
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_multiple_inputs_options()};
+    po::options_description opts_output{add_output_options()};
 
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filenames", po::value<std::vector<std::string>>(), "OSM input files")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filenames", -1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_files(vm);
     setup_output_file(vm);
 
@@ -107,6 +106,7 @@ bool CommandSort::run() {
     m_vout << "Closing output file...\n";
     writer.close();
 
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_sort.hpp b/src/command_sort.hpp
index ae07531..68a89bf 100644
--- a/src/command_sort.hpp
+++ b/src/command_sort.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -42,6 +42,14 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "sort";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium sort [OPTIONS] OSM-FILE...";
+    }
+
 }; // class CommandSort
 
 
diff --git a/src/command_time_filter.cpp b/src/command_time_filter.cpp
index 3771fb2..759a10c 100644
--- a/src/command_time_filter.cpp
+++ b/src/command_time_filter.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -34,21 +34,22 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "exception.hpp"
 
 bool CommandTimeFilter::setup(const std::vector<std::string>& arguments) {
-    po::options_description cmdline("Allowed options");
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+    po::options_description opts_output{add_output_options()};
 
-    add_common_options(cmdline);
-    add_single_input_options(cmdline);
-    add_output_options(cmdline);
-
-    po::options_description hidden("Hidden options");
+    po::options_description hidden;
     hidden.add_options()
     ("input-filename", po::value<std::string>(), "OSM input file")
     ("time-from", po::value<std::string>(), "Start of time range")
     ("time-to", po::value<std::string>(), "End of time range")
     ;
 
-    po::options_description desc("Allowed options");
-    desc.add(cmdline).add(hidden);
+    po::options_description desc;
+    desc.add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
 
     po::positional_options_description positional;
     positional.add("input-filename", 1);
@@ -56,10 +57,10 @@ bool CommandTimeFilter::setup(const std::vector<std::string>& arguments) {
     positional.add("time-to", 1);
 
     po::variables_map vm;
-    po::store(po::command_line_parser(arguments).options(desc).positional(positional).run(), vm);
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
     po::notify(vm);
 
-    setup_common(vm);
+    setup_common(vm, desc);
     setup_input_file(vm);
     setup_output_file(vm);
 
@@ -150,6 +151,7 @@ bool CommandTimeFilter::run() {
     m_vout << "Closing input file...\n";
     reader.close();
 
+    show_memory_used();
     m_vout << "Done.\n";
 
     return true;
diff --git a/src/command_time_filter.hpp b/src/command_time_filter.hpp
index 644accc..111dbed 100644
--- a/src/command_time_filter.hpp
+++ b/src/command_time_filter.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -45,6 +45,15 @@ public:
 
     bool run() override final;
 
+    const char* name() const noexcept override final {
+        return "time-filter";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium time-filter [OPTIONS] OSM-HISTORY-FILE [TIME]\n"
+               "       osmium time-filter [OPTIONS] OSM-HISTORY-FILE FROM-TIME TO-TIME";
+    }
+
 }; // class CommandTimeFilter
 
 
diff --git a/src/exception.hpp b/src/exception.hpp
index 4981485..d63a393 100644
--- a/src/exception.hpp
+++ b/src/exception.hpp
@@ -6,7 +6,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/io.cpp b/src/io.cpp
index 50b0be3..5698beb 100644
--- a/src/io.cpp
+++ b/src/io.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -39,10 +39,14 @@ void with_single_osm_input::setup_input_file(const boost::program_options::varia
     m_input_file = osmium::io::File(m_input_filename, m_input_format);
 }
 
-void with_single_osm_input::add_single_input_options(po::options_description& options) {
+po::options_description with_single_osm_input::add_single_input_options() {
+    po::options_description options{"INPUT OPTIONS"};
+
     options.add_options()
     ("input-format,F", po::value<std::string>(), "Format of input file")
     ;
+
+    return options;
 }
 
 void with_single_osm_input::show_single_input_arguments(osmium::util::VerboseOutput& vout) {
@@ -51,10 +55,10 @@ void with_single_osm_input::show_single_input_arguments(osmium::util::VerboseOut
     vout << "    file format: " << m_input_format << "\n";
 }
 
-void with_multiple_osm_inputs::setup_input_files(const boost::program_options::variables_map& vm) {
+void with_multiple_osm_inputs::setup_input_files(const boost::program_options::variables_map& vm, bool optional) {
     if (vm.count("input-filenames")) {
         m_input_filenames = vm["input-filenames"].as<std::vector<std::string>>();
-    } else {
+    } else if (!optional) {
         m_input_filenames.push_back("-"); // default is stdin
     }
 
@@ -80,10 +84,15 @@ void with_multiple_osm_inputs::setup_input_files(const boost::program_options::v
     }
 }
 
-void with_multiple_osm_inputs::add_multiple_inputs_options(po::options_description& options) {
+
+po::options_description with_multiple_osm_inputs::add_multiple_inputs_options() {
+    po::options_description options{"INPUT OPTIONS"};
+
     options.add_options()
     ("input-format,F", po::value<std::string>(), "Format of input files")
     ;
+
+    return options;
 }
 
 void with_multiple_osm_inputs::show_multiple_inputs_arguments(osmium::util::VerboseOutput& vout) {
@@ -128,15 +137,19 @@ void with_osm_output::setup_output_file(const po::variables_map& vm) {
     m_output_file.check();
 }
 
-void with_osm_output::add_output_options(po::options_description& options) {
+po::options_description with_osm_output::add_output_options() {
+    po::options_description options("OUTPUT OPTIONS");
+
     options.add_options()
+    ("output-format,f", po::value<std::string>(), "Format of output file")
+    ("fsync", "Call fsync after writing file")
     ("generator", po::value<std::string>(), "Generator setting for file header")
     ("output,o", po::value<std::string>(), "Output file")
-    ("output-format,f", po::value<std::string>(), "Format of output file")
-    ("output-header", po::value<std::vector<std::string>>(), "Add output header")
     ("overwrite,O", "Allow existing output file to be overwritten")
-    ("fsync", "Call fsync after writing file")
+    ("output-header", po::value<std::vector<std::string>>(), "Add output header")
     ;
+
+    return options;
 }
 
 void with_osm_output::show_output_arguments(osmium::util::VerboseOutput& vout) {
diff --git a/src/main.cpp b/src/main.cpp
index f732fe4..d079173 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,7 +3,7 @@
 Osmium -- OpenStreetMap data manipulation command line tool
 http://osmcode.org/osmium
 
-Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # include <io.h>
 #endif
 
+#include <osmium/version.hpp>
+
 #include "cmd.hpp"
 
 enum return_code : int {
@@ -76,8 +78,9 @@ int main(int argc, char *argv[]) {
     }
 
     if (command == "version") {
-        std::cout << "osmium version " << OSMIUM_VERSION << "\n"
-                  << "Copyright (C) 2013-2015  Jochen Topf <jochen at topf.org>\n"
+        std::cout << "osmium version " OSMIUM_VERSION "\n"
+                  << "libosmium version " LIBOSMIUM_VERSION_STRING "\n"
+                  << "Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>\n"
                   << "License: GNU GENERAL PUBLIC LICENSE Version 3 <http://gnu.org/licenses/gpl.html>.\n"
                   << "This is free software: you are free to change and redistribute it.\n"
                   << "There is NO WARRANTY, to the extent permitted by law.\n";
@@ -88,7 +91,7 @@ int main(int argc, char *argv[]) {
     std::unique_ptr<Command> cmd = CommandFactory::instance().create_command(command);
 
     if (!cmd) {
-        std::cerr << "Unknown command '" << command << "'. Try 'osmium help'." << std::endl;
+        std::cerr << "Unknown command or option '" << command << "'. Try 'osmium help'.\n";
         return return_code::fatal;
     }
 
@@ -110,6 +113,8 @@ int main(int argc, char *argv[]) {
         if (cmd->run()) {
             return return_code::okay;
         }
+    } catch (std::bad_alloc& e) {
+        std::cerr << "Out of memory. Read the MEMORY USAGE section of the osmium(1) manpage.\n";
     } catch (std::exception& e) {
         std::cerr << e.what() << "\n";
     }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 456b4d1..c30d8e4 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -11,7 +11,7 @@ include_directories(../src)
 include_directories(../include)
 
 file(GLOB ALL_SETUP_TESTS */test_setup.cpp)
-file(GLOB ALL_COMMANDS ../src/command_*.cpp ../src/io.cpp ../src/cmd_factory.cpp)
+file(GLOB ALL_COMMANDS ../src/command_*.cpp ../src/io.cpp ../src/cmd.cpp ../src/cmd_factory.cpp)
 add_executable(unit_tests unit_tests.cpp ${ALL_COMMANDS} ${ALL_SETUP_TESTS})
 target_link_libraries(unit_tests ${Boost_LIBRARIES} ${OSMIUM_LIBRARIES})
 add_test(NAME unit_tests COMMAND unit_tests)
@@ -47,7 +47,7 @@ function(check_output _dir _name _command _reference)
     )
 endfunction()
 
-function(check_output2 _dir _name _command1 _command2 _reference)
+function(check_output2 _dir _name _tmpdir _command1 _command2 _reference)
     set(_cmd1 "$<TARGET_FILE:osmium> ${_command1}")
     set(_cmd2 "$<TARGET_FILE:osmium> ${_command2}")
     add_test(
@@ -56,6 +56,7 @@ function(check_output2 _dir _name _command1 _command2 _reference)
         -D cmd:FILEPATH=${_cmd1}
         -D cmd2:FILEPATH=${_cmd2}
         -D dir:PATH=${PROJECT_SOURCE_DIR}/test
+        -D tmpdir:PATH=${_tmpdir}
         -D reference:FILEPATH=${PROJECT_SOURCE_DIR}/test/${_reference}
         -D output:FILEPATH=${PROJECT_BINARY_DIR}/test/${_dir}/cmd-output-${_name}
         -P ${CMAKE_SOURCE_DIR}/cmake/run_test_compare_output.cmake
@@ -65,6 +66,28 @@ endfunction()
 
 #-----------------------------------------------------------------------------
 #
+#  Check "osmium SUBCOMMAND -h" for most subcommands.
+#
+#-----------------------------------------------------------------------------
+function(check_cmd_help _command)
+    do_test(help_cmd_${_command} "osmium ${_command} -h" "Usage: osmium ${_command}.*OPTIONS:")
+endfunction()
+
+check_cmd_help(apply-changes)
+check_cmd_help(cat)
+check_cmd_help(changeset-filter)
+check_cmd_help(check-refs)
+check_cmd_help(fileinfo)
+check_cmd_help(getid)
+check_cmd_help(merge-changes)
+check_cmd_help(renumber)
+check_cmd_help(show)
+check_cmd_help(sort)
+check_cmd_help(time-filter)
+
+
+#-----------------------------------------------------------------------------
+#
 #  Configure tests in all subdirectories
 #
 #-----------------------------------------------------------------------------
diff --git a/test/add-locations-to-ways/CMakeLists.txt b/test/add-locations-to-ways/CMakeLists.txt
new file mode 100644
index 0000000..739364e
--- /dev/null
+++ b/test/add-locations-to-ways/CMakeLists.txt
@@ -0,0 +1,17 @@
+#-----------------------------------------------------------------------------
+#
+#  CMake Config
+#
+#  Osmium Tool Tests - add-locations-to-ways
+#
+#-----------------------------------------------------------------------------
+
+function(check_add_locations_to_ways _name _options _input _output)
+    check_output(add-locations-to-ways ${_name} "add-locations-to-ways ${_options} --generator=test --output-format=xml add-locations-to-ways/${_input}" "add-locations-to-ways/${_output}")
+endfunction()
+
+check_add_locations_to_ways(taggednodes "" input.osm output.osm)
+check_add_locations_to_ways(allnodes "-n" input.osm output-n.osm)
+
+
+#-----------------------------------------------------------------------------
diff --git a/test/sort/output-bounds.osm b/test/add-locations-to-ways/input.osm
similarity index 68%
copy from test/sort/output-bounds.osm
copy to test/add-locations-to-ways/input.osm
index 6637420..66d2738 100644
--- a/test/sort/output-bounds.osm
+++ b/test/add-locations-to-ways/input.osm
@@ -1,10 +1,11 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="test">
-  <bounds minlon="0.0000000" minlat="0.0000000" maxlon="15.0000000" maxlat="15.0000000"/>
+<osm version="0.6" upload="false" generator="testdata">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
-  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1"/>
+  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1">
+    <tag k="some" v="tag"/>
+  </node>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
@@ -16,8 +17,4 @@
     <nd ref="13"/>
     <tag k="xyz" v="abc"/>
   </way>
-  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="12" role="m1"/>
-    <member type="way" ref="20" role="m2"/>
-  </relation>
 </osm>
diff --git a/test/sort/output-bounds.osm b/test/add-locations-to-ways/output-n.osm
similarity index 63%
copy from test/sort/output-bounds.osm
copy to test/add-locations-to-ways/output-n.osm
index 6637420..087d29e 100644
--- a/test/sort/output-bounds.osm
+++ b/test/add-locations-to-ways/output-n.osm
@@ -1,23 +1,20 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="test">
-  <bounds minlon="0.0000000" minlat="0.0000000" maxlon="15.0000000" maxlat="15.0000000"/>
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
-  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1"/>
+  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1">
+    <tag k="some" v="tag"/>
+  </node>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <nd ref="10"/>
-    <nd ref="11"/>
-    <nd ref="12"/>
+    <nd ref="10" lat="1" lon="1"/>
+    <nd ref="11" lat="2" lon="1"/>
+    <nd ref="12" lat="3" lon="1"/>
     <tag k="foo" v="bar"/>
   </way>
   <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <nd ref="12"/>
-    <nd ref="13"/>
+    <nd ref="12" lat="3" lon="1"/>
+    <nd ref="13" lat="4" lon="1"/>
     <tag k="xyz" v="abc"/>
   </way>
-  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="12" role="m1"/>
-    <member type="way" ref="20" role="m2"/>
-  </relation>
 </osm>
diff --git a/test/add-locations-to-ways/output.osm b/test/add-locations-to-ways/output.osm
new file mode 100644
index 0000000..582e2d4
--- /dev/null
+++ b/test/add-locations-to-ways/output.osm
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="test">
+  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1">
+    <tag k="some" v="tag"/>
+  </node>
+  <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="10" lat="1" lon="1"/>
+    <nd ref="11" lat="2" lon="1"/>
+    <nd ref="12" lat="3" lon="1"/>
+    <tag k="foo" v="bar"/>
+  </way>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12" lat="3" lon="1"/>
+    <nd ref="13" lat="4" lon="1"/>
+    <tag k="xyz" v="abc"/>
+  </way>
+</osm>
diff --git a/test/cat/output1.osm.opl b/test/cat/output1.osm.opl
index bd74966..f2518fe 100644
--- a/test/cat/output1.osm.opl
+++ b/test/cat/output1.osm.opl
@@ -1,3 +1,3 @@
-n1 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T x1.0000000 y1.0000000
-n2 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T x1.0000000 y2.0000000
-n3 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T x1.0000000 y3.0000000
+n1 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T x1 y1
+n2 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T x1 y2
+n3 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T x1 y3
diff --git a/test/changeset-filter/output1-all.osm b/test/changeset-filter/output1-all.osm
index 5c73046..53021da 100644
--- a/test/changeset-filter/output1-all.osm
+++ b/test/changeset-filter/output1-all.osm
@@ -1,6 +1,6 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="test">
- <changeset id="15449957" created_at="2013-03-22T02:08:55Z" closed_at="2013-03-22T02:08:58Z" open="false" user="Elbert" uid="1237205" min_lat="-10.0004425" min_lon="120.2988730" max_lat="-10.0002384" max_lon="120.2991740" num_changes="10" comments_count="0">
+ <changeset id="15449957" created_at="2013-03-22T02:08:55Z" closed_at="2013-03-22T02:08:58Z" open="false" user="Elbert" uid="1237205" min_lat="-10.0004425" min_lon="120.298873" max_lat="-10.0002384" max_lon="120.299174" num_changes="10" comments_count="0">
   <tag k="created_by" v="JOSM/1.5 (5356 en)"/>
  </changeset>
  <changeset id="15449958" created_at="2013-03-22T02:09:11Z" closed_at="2013-03-22T03:09:11Z" open="false" user="sree dinesh" uid="1233268" num_changes="0" comments_count="0">
diff --git a/test/changeset-filter/output1-first.osm b/test/changeset-filter/output1-first.osm
index 27dca96..cb78a77 100644
--- a/test/changeset-filter/output1-first.osm
+++ b/test/changeset-filter/output1-first.osm
@@ -1,6 +1,6 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="test">
- <changeset id="15449957" created_at="2013-03-22T02:08:55Z" closed_at="2013-03-22T02:08:58Z" open="false" user="Elbert" uid="1237205" min_lat="-10.0004425" min_lon="120.2988730" max_lat="-10.0002384" max_lon="120.2991740" num_changes="10" comments_count="0">
+ <changeset id="15449957" created_at="2013-03-22T02:08:55Z" closed_at="2013-03-22T02:08:58Z" open="false" user="Elbert" uid="1237205" min_lat="-10.0004425" min_lon="120.298873" max_lat="-10.0002384" max_lon="120.299174" num_changes="10" comments_count="0">
   <tag k="created_by" v="JOSM/1.5 (5356 en)"/>
  </changeset>
 </osm>
diff --git a/test/check-refs/CMakeLists.txt b/test/check-refs/CMakeLists.txt
index 97bb832..167c0ca 100644
--- a/test/check-refs/CMakeLists.txt
+++ b/test/check-refs/CMakeLists.txt
@@ -6,8 +6,48 @@
 #
 #-----------------------------------------------------------------------------
 
-add_test(NAME check-ref-nwr-okay COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/nwr-okay.osm)
-add_test(NAME check-ref-nw-okay COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/check-refs/nw-okay.osm)
+add_test(NAME check-ref-w-okay COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/check-refs/okay.osm)
+add_test(NAME check-ref-r-okay COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/okay.osm)
+
+add_test(NAME check-ref-fail-n-in-w COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/check-refs/fail-n-in-w.osm)
+set_tests_properties(check-ref-fail-n-in-w PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-w-way-okay COMMAND osmium check-refs    ${CMAKE_SOURCE_DIR}/test/check-refs/way-okay.osm)
+add_test(NAME check-ref-r-way-okay COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/way-okay.osm)
+set_tests_properties(check-ref-r-way-okay PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-okay-r-in-r COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/okay-r-in-r.osm)
+
+add_test(NAME check-ref-fail-n-in-r COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/fail-n-in-r.osm)
+set_tests_properties(check-ref-fail-n-in-r PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-w-in-r COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/fail-w-in-r.osm)
+set_tests_properties(check-ref-fail-w-in-r PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-r-in-r-1 COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/fail-r-in-r-1.osm)
+set_tests_properties(check-ref-fail-r-in-r-1 PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-r-in-r-2 COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/check-refs/fail-r-in-r-2.osm)
+set_tests_properties(check-ref-fail-r-in-r-2 PROPERTIES WILL_FAIL true)
+
+#-----------------------------------------------------------------------------
+
+# input data not ordered properly
+
+add_test(NAME check-ref-fail-order-n COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/order/fail-order-n.osm)
+set_tests_properties(check-ref-fail-order-n PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-order-w COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/order/fail-order-w.osm)
+set_tests_properties(check-ref-fail-order-w PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-order-r COMMAND osmium check-refs -r ${CMAKE_SOURCE_DIR}/test/order/fail-order-r.osm)
+set_tests_properties(check-ref-fail-order-r PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-order-wn COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/order/fail-order-wn.osm)
+set_tests_properties(check-ref-fail-order-wn PROPERTIES WILL_FAIL true)
+
+add_test(NAME check-ref-fail-order-rw COMMAND osmium check-refs ${CMAKE_SOURCE_DIR}/test/order/fail-order-rw.osm)
+set_tests_properties(check-ref-fail-order-rw PROPERTIES WILL_FAIL true)
 
 
 #-----------------------------------------------------------------------------
diff --git a/test/check-refs/fail-n-in-r.osm b/test/check-refs/fail-n-in-r.osm
new file mode 100644
index 0000000..5b2efc6
--- /dev/null
+++ b/test/check-refs/fail-n-in-r.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="10" role=""/>
+  </relation>
+</osm>
diff --git a/test/check-refs/nw-okay.osm b/test/check-refs/fail-n-in-w.osm
similarity index 58%
copy from test/check-refs/nw-okay.osm
copy to test/check-refs/fail-n-in-w.osm
index 2ae8a8b..0127f58 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/check-refs/fail-n-in-w.osm
@@ -1,15 +1,9 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="testdata" upload="false">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
-  <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
-    <nd ref="12"/>
   </way>
-  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="10" role=""/>
-    <member type="way" ref="21" role=""/>
-  </relation>
 </osm>
diff --git a/test/check-refs/fail-r-in-r-1.osm b/test/check-refs/fail-r-in-r-1.osm
new file mode 100644
index 0000000..661aa21
--- /dev/null
+++ b/test/check-refs/fail-r-in-r-1.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="31" role=""/>
+  </relation>
+</osm>
diff --git a/test/check-refs/fail-r-in-r-2.osm b/test/check-refs/fail-r-in-r-2.osm
new file mode 100644
index 0000000..79b7293
--- /dev/null
+++ b/test/check-refs/fail-r-in-r-2.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
+  </relation>
+</osm>
diff --git a/test/check-refs/fail-w-in-r.osm b/test/check-refs/fail-w-in-r.osm
new file mode 100644
index 0000000..dc4311f
--- /dev/null
+++ b/test/check-refs/fail-w-in-r.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="way" ref="20" role=""/>
+  </relation>
+</osm>
diff --git a/test/check-refs/okay-r-in-r.osm b/test/check-refs/okay-r-in-r.osm
new file mode 100644
index 0000000..35fbfe4
--- /dev/null
+++ b/test/check-refs/okay-r-in-r.osm
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="31" role=""/>
+  </relation>
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
+  </relation>
+</osm>
diff --git a/test/check-refs/nw-okay.osm b/test/check-refs/okay.osm
similarity index 82%
copy from test/check-refs/nw-okay.osm
copy to test/check-refs/okay.osm
index 2ae8a8b..f5799c6 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/check-refs/okay.osm
@@ -6,10 +6,14 @@
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
+  </way>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
+    <nd ref="11"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <member type="node" ref="10" role=""/>
     <member type="way" ref="21" role=""/>
+    <member type="way" ref="20" role=""/>
   </relation>
 </osm>
diff --git a/test/check-refs/nw-okay.osm b/test/check-refs/way-okay.osm
similarity index 100%
copy from test/check-refs/nw-okay.osm
copy to test/check-refs/way-okay.osm
index 2ae8a8b..4dfd6ea 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/check-refs/way-okay.osm
@@ -5,8 +5,8 @@
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
-    <nd ref="11"/>
     <nd ref="12"/>
+    <nd ref="11"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <member type="node" ref="10" role=""/>
diff --git a/test/formats/f1.osm.opl b/test/formats/f1.osm.opl
index 56462e8..8c650bc 100644
--- a/test/formats/f1.osm.opl
+++ b/test/formats/f1.osm.opl
@@ -1,7 +1,7 @@
-n10 v1 dV c1 t2010-01-01T00:00:00Z i1 utest T x1.0000000 y1.0000000
-n11 v1 dV c1 t2012-01-01T22:00:00Z i0 u T x1.2355000 y2.0345230
-n12 v1 dV c2 t2013-12-01T11:11:11Z i3 ufoo T x1.0000000 y3.0000000
-n13 v1 dV c3 t2015-01-01T01:00:00Z i1 utest T x1.0000000 y4.0000000
+n10 v1 dV c1 t2010-01-01T00:00:00Z i1 utest T x1 y1
+n11 v1 dV c1 t2012-01-01T22:00:00Z i0 u T x1.2355 y2.034523
+n12 v1 dV c2 t2013-12-01T11:11:11Z i3 ufoo T x1 y3
+n13 v1 dV c3 t2015-01-01T01:00:00Z i1 utest T x1 y4
 w20 v1 dV c4 t2015-01-01T01:00:00Z i1 utest Tfoo=bar,=bar,xyz=,!%40%$=*#/ Nn10,n11,n12
 w21 v1 dV c1 t2015-01-01T01:00:00Z i1 utest T Nn12,n13
 r30 v1 dV c1 t2015-01-01T01:00:00Z i1 utest Txyz=abc Mn12@,w20 at some%20%way
diff --git a/test/getid/CMakeLists.txt b/test/getid/CMakeLists.txt
index a4d9a03..100a985 100644
--- a/test/getid/CMakeLists.txt
+++ b/test/getid/CMakeLists.txt
@@ -10,6 +10,44 @@ function(check_getid _name _input _output)
     check_output(getid ${_name} "getid --generator=test -f osm getid/${_input} n11,n12 w21" "getid/${_output}")
 endfunction()
 
+function(check_getid_file _name _file _input _output)
+    check_output(getid ${_name} "getid --generator=test -i getid/${_file} -f osm getid/${_input}" "getid/${_output}")
+endfunction()
+
 check_getid(n input.osm output.osm)
+check_getid_file(file1 idfile input.osm output.osm)
+
+#-----------------------------------------------------------------------------
+
+function(check_getid_r _name _source _input _output)
+    check_output(getid ${_name}   "getid -r --generator=test -f osm getid/${_source}.osm -I getid/${_input}.osm" "getid/${_output}.osm")
+    check_output(getid ${_name}-i "getid -r --generator=test -f osm getid/${_source}.osm -i getid/${_input}.id"  "getid/${_output}.osm")
+endfunction()
+
+check_getid_r(n10 source in10 out10)
+check_getid_r(w21 source in21 out21)
+check_getid_r(r30 source in30 out30)
+check_getid_r(r31 source in31 out31)
+check_getid_r(r32 source in32 out32)
+
+check_getid_r(n10nrr source-no-rr in10 out10)
+check_getid_r(w21nrr source-no-rr in21 out21)
+check_getid_r(r30nrr source-no-rr in30 out30)
+check_getid_r(r32nrr source-no-rr in32 out32)
+
+check_getid_r(missing-n19 source in19 out31)
+set_tests_properties(getid-missing-n19 PROPERTIES WILL_FAIL true)
+set_tests_properties(getid-missing-n19-i PROPERTIES WILL_FAIL true)
+
+check_getid_r(missing-w29 source in29 out31)
+set_tests_properties(getid-missing-w29 PROPERTIES WILL_FAIL true)
+set_tests_properties(getid-missing-w29-i PROPERTIES WILL_FAIL true)
+
+check_getid_r(missing-r39 source in39 out31)
+set_tests_properties(getid-missing-r39 PROPERTIES WILL_FAIL true)
+set_tests_properties(getid-missing-r39-i PROPERTIES WILL_FAIL true)
+
+check_getid_r(relloop relloop relloop relloop-out)
+
 
 #-----------------------------------------------------------------------------
diff --git a/test/getid/idfile b/test/getid/idfile
new file mode 100644
index 0000000..4e5d94d
--- /dev/null
+++ b/test/getid/idfile
@@ -0,0 +1,5 @@
+n11
+
+n12 foo
+# comment
+w21
diff --git a/test/getid/in10.id b/test/getid/in10.id
new file mode 100644
index 0000000..bae43e4
--- /dev/null
+++ b/test/getid/in10.id
@@ -0,0 +1 @@
+n10
diff --git a/test/getid/in10.osm b/test/getid/in10.osm
new file mode 100644
index 0000000..463349f
--- /dev/null
+++ b/test/getid/in10.osm
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
+</osm>
diff --git a/test/getid/in19.id b/test/getid/in19.id
new file mode 100644
index 0000000..02688b2
--- /dev/null
+++ b/test/getid/in19.id
@@ -0,0 +1 @@
+n19
diff --git a/test/getid/in19.osm b/test/getid/in19.osm
new file mode 100644
index 0000000..7f2431f
--- /dev/null
+++ b/test/getid/in19.osm
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <node id="19" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
+</osm>
diff --git a/test/getid/in21.id b/test/getid/in21.id
new file mode 100644
index 0000000..7e523c1
--- /dev/null
+++ b/test/getid/in21.id
@@ -0,0 +1 @@
+w21
diff --git a/test/getid/in21.osm b/test/getid/in21.osm
new file mode 100644
index 0000000..e141293
--- /dev/null
+++ b/test/getid/in21.osm
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
+</osm>
diff --git a/test/getid/in29.id b/test/getid/in29.id
new file mode 100644
index 0000000..2582df0
--- /dev/null
+++ b/test/getid/in29.id
@@ -0,0 +1 @@
+w29
diff --git a/test/getid/in29.osm b/test/getid/in29.osm
new file mode 100644
index 0000000..4efa37a
--- /dev/null
+++ b/test/getid/in29.osm
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <way id="29" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
+</osm>
diff --git a/test/getid/in30.id b/test/getid/in30.id
new file mode 100644
index 0000000..ad2f182
--- /dev/null
+++ b/test/getid/in30.id
@@ -0,0 +1 @@
+r30
diff --git a/test/getid/in30.osm b/test/getid/in30.osm
new file mode 100644
index 0000000..f7ed698
--- /dev/null
+++ b/test/getid/in30.osm
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="10" role=""/>
+    <member type="way" ref="21" role=""/>
+    <member type="way" ref="20" role=""/>
+  </relation>
+</osm>
diff --git a/test/getid/in31.id b/test/getid/in31.id
new file mode 100644
index 0000000..f7205a2
--- /dev/null
+++ b/test/getid/in31.id
@@ -0,0 +1 @@
+r31
diff --git a/test/getid/in31.osm b/test/getid/in31.osm
new file mode 100644
index 0000000..79b7293
--- /dev/null
+++ b/test/getid/in31.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
+  </relation>
+</osm>
diff --git a/test/getid/in32.id b/test/getid/in32.id
new file mode 100644
index 0000000..b5b8e23
--- /dev/null
+++ b/test/getid/in32.id
@@ -0,0 +1 @@
+r32
diff --git a/test/getid/in32.osm b/test/getid/in32.osm
new file mode 100644
index 0000000..3c3cd46
--- /dev/null
+++ b/test/getid/in32.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="32" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="13" role=""/>
+  </relation>
+</osm>
diff --git a/test/getid/in39.id b/test/getid/in39.id
new file mode 100644
index 0000000..936bff3
--- /dev/null
+++ b/test/getid/in39.id
@@ -0,0 +1 @@
+r39
diff --git a/test/getid/in39.osm b/test/getid/in39.osm
new file mode 100644
index 0000000..6a38e54
--- /dev/null
+++ b/test/getid/in39.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="39" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="13" role=""/>
+  </relation>
+</osm>
diff --git a/test/getid/out10.osm b/test/getid/out10.osm
new file mode 100644
index 0000000..3aae281
--- /dev/null
+++ b/test/getid/out10.osm
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="test">
+  <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
+</osm>
diff --git a/test/getid/out21.osm b/test/getid/out21.osm
new file mode 100644
index 0000000..86afa13
--- /dev/null
+++ b/test/getid/out21.osm
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="test">
+  <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
+  <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
+</osm>
diff --git a/test/check-refs/nw-okay.osm b/test/getid/out30.osm
similarity index 78%
copy from test/check-refs/nw-okay.osm
copy to test/getid/out30.osm
index 2ae8a8b..0e21ed4 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/getid/out30.osm
@@ -1,15 +1,19 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="testdata" upload="false">
+<osm version="0.6" generator="test">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
+  </way>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
+    <nd ref="11"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <member type="node" ref="10" role=""/>
     <member type="way" ref="21" role=""/>
+    <member type="way" ref="20" role=""/>
   </relation>
 </osm>
diff --git a/test/sort/output-bounds.osm b/test/getid/out31.osm
similarity index 67%
copy from test/sort/output-bounds.osm
copy to test/getid/out31.osm
index 6637420..4835756 100644
--- a/test/sort/output-bounds.osm
+++ b/test/getid/out31.osm
@@ -1,23 +1,22 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="test">
-  <bounds minlon="0.0000000" minlat="0.0000000" maxlon="15.0000000" maxlat="15.0000000"/>
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
-  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1"/>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
-    <nd ref="12"/>
-    <tag k="foo" v="bar"/>
   </way>
   <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
-    <nd ref="13"/>
-    <tag k="xyz" v="abc"/>
+    <nd ref="11"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="12" role="m1"/>
-    <member type="way" ref="20" role="m2"/>
+    <member type="node" ref="10" role=""/>
+    <member type="way" ref="21" role=""/>
+    <member type="way" ref="20" role=""/>
+  </relation>
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
   </relation>
 </osm>
diff --git a/test/getid/out32.osm b/test/getid/out32.osm
new file mode 100644
index 0000000..2d9d162
--- /dev/null
+++ b/test/getid/out32.osm
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="test">
+  <node id="13" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="4" lon="1"/>
+  <relation id="32" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="13" role=""/>
+  </relation>
+</osm>
diff --git a/test/getid/relloop-out.osm b/test/getid/relloop-out.osm
new file mode 100644
index 0000000..343a18d
--- /dev/null
+++ b/test/getid/relloop-out.osm
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="test">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="31" role=""/>
+  </relation>
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
+  </relation>
+</osm>
diff --git a/test/getid/relloop.id b/test/getid/relloop.id
new file mode 100644
index 0000000..25278e0
--- /dev/null
+++ b/test/getid/relloop.id
@@ -0,0 +1,2 @@
+r30
+r31
diff --git a/test/getid/relloop.osm b/test/getid/relloop.osm
new file mode 100644
index 0000000..35fbfe4
--- /dev/null
+++ b/test/getid/relloop.osm
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="31" role=""/>
+  </relation>
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
+  </relation>
+</osm>
diff --git a/test/sort/output-bounds.osm b/test/getid/source-no-rr.osm
similarity index 66%
copy from test/sort/output-bounds.osm
copy to test/getid/source-no-rr.osm
index 6637420..ac6d706 100644
--- a/test/sort/output-bounds.osm
+++ b/test/getid/source-no-rr.osm
@@ -1,6 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="test">
-  <bounds minlon="0.0000000" minlat="0.0000000" maxlon="15.0000000" maxlat="15.0000000"/>
+<osm version="0.6" generator="testdata" upload="false">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
@@ -8,16 +7,21 @@
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
-    <nd ref="12"/>
-    <tag k="foo" v="bar"/>
   </way>
   <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
+  <way id="22" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
     <nd ref="13"/>
-    <tag k="xyz" v="abc"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="12" role="m1"/>
-    <member type="way" ref="20" role="m2"/>
+    <member type="node" ref="10" role=""/>
+    <member type="way" ref="21" role=""/>
+    <member type="way" ref="20" role=""/>
+  </relation>
+  <relation id="32" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="13" role=""/>
   </relation>
 </osm>
diff --git a/test/sort/output-bounds.osm b/test/getid/source.osm
similarity index 59%
copy from test/sort/output-bounds.osm
copy to test/getid/source.osm
index 6637420..d7e7916 100644
--- a/test/sort/output-bounds.osm
+++ b/test/getid/source.osm
@@ -1,6 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="test">
-  <bounds minlon="0.0000000" minlat="0.0000000" maxlon="15.0000000" maxlat="15.0000000"/>
+<osm version="0.6" generator="testdata" upload="false">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
@@ -8,16 +7,24 @@
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
-    <nd ref="12"/>
-    <tag k="foo" v="bar"/>
   </way>
   <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
+  <way id="22" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
     <nd ref="13"/>
-    <tag k="xyz" v="abc"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="12" role="m1"/>
-    <member type="way" ref="20" role="m2"/>
+    <member type="node" ref="10" role=""/>
+    <member type="way" ref="21" role=""/>
+    <member type="way" ref="20" role=""/>
+  </relation>
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="relation" ref="30" role=""/>
+  </relation>
+  <relation id="32" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="13" role=""/>
   </relation>
 </osm>
diff --git a/test/help/CMakeLists.txt b/test/help/CMakeLists.txt
index 19cdc5a..7bfff68 100644
--- a/test/help/CMakeLists.txt
+++ b/test/help/CMakeLists.txt
@@ -6,10 +6,10 @@
 #
 #-----------------------------------------------------------------------------
 
-do_test(help1 "osmium"        "^Usage: .*Commands are:")
-do_test(help2 "osmium help"   "^Usage: .*Commands are:")
-do_test(help3 "osmium --help" "^Usage: .*Commands are:")
-do_test(help4 "osmium -h"     "^Usage: .*Commands are:")
+do_test(help1 "osmium"        "^Usage: .*COMMANDS:")
+do_test(help2 "osmium help"   "^Usage: .*COMMANDS:")
+do_test(help3 "osmium --help" "^Usage: .*COMMANDS:")
+do_test(help4 "osmium -h"     "^Usage: .*COMMANDS:")
 
 if(PANDOC)
     do_test(help_cat "osmium help cat" "^OSMIUM-CAT\\(1\\)")
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index de61226..f042149 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.2.1
- *  Generated: 2015-06-30 18:23:27.961086
+ *  Catch v1.3.3
+ *  Generated: 2016-01-22 07:51:36.661106
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -21,8 +21,6 @@
 
 // #included from: internal/catch_suppress_warnings.h
 
-#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
-
 #ifdef __clang__
 #   ifdef __ICC // icpc defines the __clang__ macro
 #       pragma warning(push)
@@ -37,6 +35,7 @@
 #       pragma clang diagnostic ignored "-Wc++98-compat"
 #       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
 #       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
 #    endif
 #elif defined __GNUC__
 #    pragma GCC diagnostic ignored "-Wvariadic-macros"
@@ -44,7 +43,6 @@
 #    pragma GCC diagnostic push
 #    pragma GCC diagnostic ignored "-Wpadded"
 #endif
-
 #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
 #  define CATCH_IMPL
 #endif
@@ -84,11 +82,19 @@
 // CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
 // CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
 // CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
 
 // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
 
 // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
 
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
 // In general each macro has a _NO_<feature name> form
 // (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
 // Many features, at point of detection, define an _INTERNAL_ macro, so they
@@ -130,10 +136,13 @@
 // GCC
 #ifdef __GNUC__
 
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
 #   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
 #endif
 
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
 #endif // __GNUC__
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -142,6 +151,7 @@
 
 #if (_MSC_VER >= 1600)
 #   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
 #endif
 
 #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
@@ -151,6 +161,8 @@
 
 #endif // _MSC_VER
 
+////////////////////////////////////////////////////////////////////////////////
+
 // Use variadic macros if the compiler supports them
 #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
     ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
@@ -165,7 +177,7 @@
 // C++ language feature support
 
 // catch all support for C++11
-#if (__cplusplus >= 201103L)
+#if defined(__cplusplus) && __cplusplus >= 201103L
 
 #  define CATCH_CPP11_OR_GREATER
 
@@ -193,6 +205,17 @@
 #    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
 #  endif
 
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+#  endif
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#  endif
+
 #endif // __cplusplus >= 201103L
 
 // Now set the actual defines based on the above + anything the user has configured
@@ -212,7 +235,16 @@
 #   define CATCH_CONFIG_CPP11_TUPLE
 #endif
 #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
-#define CATCH_CONFIG_VARIADIC_MACROS
+#   define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
 #endif
 
 // noexcept support:
@@ -224,8 +256,36 @@
 #  define CATCH_NOEXCEPT_IS(x)
 #endif
 
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_NULL nullptr
+#else
+#   define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+#   define CATCH_OVERRIDE override
+#else
+#   define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
 namespace Catch {
 
+    struct IConfig;
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
     class NonCopyable {
 #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         NonCopyable( NonCopyable const& )              = delete;
@@ -312,6 +372,9 @@ namespace Catch {
 
     void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
 
+    void seedRng( IConfig const& config );
+    unsigned int rngSeed();
+
     // Use this in variadic streaming macros to allow
     //    >> +StreamEndStop
     // as well as
@@ -397,7 +460,7 @@ namespace Catch {
     template<typename T>
     class Ptr {
     public:
-        Ptr() : m_p( NULL ){}
+        Ptr() : m_p( CATCH_NULL ){}
         Ptr( T* p ) : m_p( p ){
             if( m_p )
                 m_p->addRef();
@@ -413,7 +476,7 @@ namespace Catch {
         void reset() {
             if( m_p )
                 m_p->release();
-            m_p = NULL;
+            m_p = CATCH_NULL;
         }
         Ptr& operator = ( T* p ){
             Ptr temp( p );
@@ -426,12 +489,11 @@ namespace Catch {
             return *this;
         }
         void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
-        T* get() { return m_p; }
-        const T* get() const{ return m_p; }
+        T* get() const{ return m_p; }
         T& operator*() const { return *m_p; }
         T* operator->() const { return m_p; }
-        bool operator !() const { return m_p == NULL; }
-        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+        bool operator !() const { return m_p == CATCH_NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
 
     private:
         T* m_p;
@@ -528,9 +590,13 @@ namespace Catch {
     struct ITestCaseRegistry {
         virtual ~ITestCaseRegistry();
         virtual std::vector<TestCase> const& getAllTests() const = 0;
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
-
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
     };
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
 }
 
 namespace Catch {
@@ -563,27 +629,32 @@ struct NameAndDesc {
     const char* description;
 };
 
+void registerTestCase
+    (   ITestCase* testCase,
+        char const* className,
+        NameAndDesc const& nameAndDesc,
+        SourceLineInfo const& lineInfo );
+
 struct AutoReg {
 
-    AutoReg(    TestFunction function,
-                SourceLineInfo const& lineInfo,
-                NameAndDesc const& nameAndDesc );
+    AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc );
 
     template<typename C>
-    AutoReg(    void (C::*method)(),
-                char const* className,
-                NameAndDesc const& nameAndDesc,
-                SourceLineInfo const& lineInfo ) {
-        registerTestCase(   new MethodTestCase<C>( method ),
-                            className,
-                            nameAndDesc,
-                            lineInfo );
-    }
-
-    void registerTestCase(  ITestCase* testCase,
-                            char const* className,
-                            NameAndDesc const& nameAndDesc,
-                            SourceLineInfo const& lineInfo );
+    AutoReg
+        (   void (C::*method)(),
+            char const* className,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        registerTestCase
+            (   new MethodTestCase<C>( method ),
+                className,
+                nameAndDesc,
+                lineInfo );
+    }
 
     ~AutoReg();
 
@@ -592,6 +663,11 @@ private:
     void operator= ( AutoReg const& );
 };
 
+void registerTestCaseFunction
+    (   TestFunction function,
+        SourceLineInfo const& lineInfo,
+        NameAndDesc const& nameAndDesc );
+
 } // end namespace Catch
 
 #ifdef CATCH_CONFIG_VARIADIC_MACROS
@@ -615,6 +691,10 @@ private:
         } \
         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
 
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) );
+
 #else
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
@@ -636,6 +716,9 @@ private:
         } \
         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
 
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) );
 #endif
 
 // #included from: internal/catch_capture.hpp
@@ -758,147 +841,468 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
 namespace Catch {
+namespace Matchers {
+    namespace Impl {
 
-    struct TestFailureException{};
+    namespace Generic {
+        template<typename ExpressionT> class AllOf;
+        template<typename ExpressionT> class AnyOf;
+        template<typename ExpressionT> class Not;
+    }
 
-    template<typename T> class ExpressionLhs;
+    template<typename ExpressionT>
+    struct Matcher : SharedImpl<IShared>
+    {
+        typedef ExpressionT ExpressionType;
 
-    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+        virtual ~Matcher() {}
+        virtual Ptr<Matcher> clone() const = 0;
+        virtual bool match( ExpressionT const& expr ) const = 0;
+        virtual std::string toString() const = 0;
 
-    struct CopyableStream {
-        CopyableStream() {}
-        CopyableStream( CopyableStream const& other ) {
-            oss << other.oss.str();
-        }
-        CopyableStream& operator=( CopyableStream const& other ) {
-            oss.str("");
-            oss << other.oss.str();
-            return *this;
+        Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const;
+        Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const;
+        Generic::Not<ExpressionT> operator ! () const;
+    };
+
+    template<typename DerivedT, typename ExpressionT>
+    struct MatcherImpl : Matcher<ExpressionT> {
+
+        virtual Ptr<Matcher<ExpressionT> > clone() const {
+            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
         }
-        std::ostringstream oss;
     };
 
-    class ResultBuilder {
-    public:
-        ResultBuilder(  char const* macroName,
-                        SourceLineInfo const& lineInfo,
-                        char const* capturedExpression,
-                        ResultDisposition::Flags resultDisposition );
+    namespace Generic {
+        template<typename ExpressionT>
+        class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> {
+        public:
+            explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {}
+            Not( Not const& other ) : m_matcher( other.m_matcher ) {}
 
-        template<typename T>
-        ExpressionLhs<T const&> operator <= ( T const& operand );
-        ExpressionLhs<bool> operator <= ( bool value );
+            virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE {
+                return !m_matcher->match( expr );
+            }
 
-        template<typename T>
-        ResultBuilder& operator << ( T const& value ) {
-            m_stream.oss << value;
-            return *this;
-        }
+            virtual std::string toString() const CATCH_OVERRIDE {
+                return "not " + m_matcher->toString();
+            }
+        private:
+            Ptr< Matcher<ExpressionT> > m_matcher;
+        };
 
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+        template<typename ExpressionT>
+        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+        public:
 
-        ResultBuilder& setResultType( ResultWas::OfType result );
-        ResultBuilder& setResultType( bool result );
-        ResultBuilder& setLhs( std::string const& lhs );
-        ResultBuilder& setRhs( std::string const& rhs );
-        ResultBuilder& setOp( std::string const& op );
+            AllOf() {}
+            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
 
-        void endExpression();
+            AllOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( !m_matchers[i]->match( expr ) )
+                        return false;
+                return true;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " and ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
 
-        std::string reconstructExpression() const;
-        AssertionResult build() const;
+            AllOf operator && ( Matcher<ExpressionT> const& other ) const {
+                AllOf allOfExpr( *this );
+                allOfExpr.add( other );
+                return allOfExpr;
+            }
 
-        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
-        void captureResult( ResultWas::OfType resultType );
-        void captureExpression();
-        void react();
-        bool shouldDebugBreak() const;
-        bool allowThrows() const;
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
 
-    private:
-        AssertionInfo m_assertionInfo;
-        AssertionResultData m_data;
-        struct ExprComponents {
-            ExprComponents() : testFalse( false ) {}
-            bool testFalse;
-            std::string lhs, rhs, op;
-        } m_exprComponents;
-        CopyableStream m_stream;
+        template<typename ExpressionT>
+        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+        public:
 
-        bool m_shouldDebugBreak;
-        bool m_shouldThrow;
-    };
+            AnyOf() {}
+            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
 
-} // namespace Catch
+            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( m_matchers[i]->match( expr ) )
+                        return true;
+                return false;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " or ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
 
-// Include after due to circular dependency:
-// #included from: catch_expression_lhs.hpp
-#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+            AnyOf operator || ( Matcher<ExpressionT> const& other ) const {
+                AnyOf anyOfExpr( *this );
+                anyOfExpr.add( other );
+                return anyOfExpr;
+            }
 
-// #included from: catch_evaluate.hpp
-#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
 
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#endif
+    } // namespace Generic
 
-#include <cstddef>
+    template<typename ExpressionT>
+    Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const {
+        Generic::AllOf<ExpressionT> allOfExpr;
+        allOfExpr.add( *this );
+        allOfExpr.add( other );
+        return allOfExpr;
+    }
 
-namespace Catch {
-namespace Internal {
+    template<typename ExpressionT>
+    Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const {
+        Generic::AnyOf<ExpressionT> anyOfExpr;
+        anyOfExpr.add( *this );
+        anyOfExpr.add( other );
+        return anyOfExpr;
+    }
 
-    enum Operator {
-        IsEqualTo,
-        IsNotEqualTo,
-        IsLessThan,
-        IsGreaterThan,
-        IsLessThanOrEqualTo,
-        IsGreaterThanOrEqualTo
-    };
+    template<typename ExpressionT>
+    Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const {
+        return Generic::Not<ExpressionT>( *this );
+    }
 
-    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
-    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
-    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
-    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
-    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
-    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
-    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+    namespace StdString {
 
-    template<typename T>
-    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+        inline std::string makeString( std::string const& str ) { return str; }
+        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
 
-// nullptr_t support based on pull request #154 from Konstantin Baumann
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+            :   m_caseSensitivity( caseSensitivity ),
+                m_str( adjustString( str ) )
+            {}
+            std::string adjustString( std::string const& str ) const {
+                return m_caseSensitivity == CaseSensitive::No
+                    ? toLower( str )
+                    : str;
 
-    // So the compare overloads can be operator agnostic we convey the operator as a template
-    // enum, which is used to specialise an Evaluator for doing the comparison.
-    template<typename T1, typename T2, Operator Op>
-    class Evaluator{};
+            }
+            std::string toStringSuffix() const
+            {
+                return m_caseSensitivity == CaseSensitive::No
+                    ? " (case insensitive)"
+                    : "";
+            }
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct Equals : MatcherImpl<Equals, std::string> {
+            Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            :   m_data( str, caseSensitivity )
+            {}
+            Equals( Equals const& other ) : m_data( other.m_data ){}
+
+            virtual ~Equals();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.m_str == m_data.adjustString( expr );;
+            }
+            virtual std::string toString() const {
+                return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct Contains : MatcherImpl<Contains, std::string> {
+            Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+            Contains( Contains const& other ) : m_data( other.m_data ){}
+
+            virtual ~Contains();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos;
+            }
+            virtual std::string toString() const {
+                return "contains: \"" + m_data.m_str  + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct StartsWith : MatcherImpl<StartsWith, std::string> {
+            StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+
+            StartsWith( StartsWith const& other ) : m_data( other.m_data ){}
+
+            virtual ~StartsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return startsWith( m_data.adjustString( expr ), m_data.m_str );
+            }
+            virtual std::string toString() const {
+                return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct EndsWith : MatcherImpl<EndsWith, std::string> {
+            EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+            EndsWith( EndsWith const& other ) : m_data( other.m_data ){}
+
+            virtual ~EndsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return endsWith( m_data.adjustString( expr ), m_data.m_str );
+            }
+            virtual std::string toString() const {
+                return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+    } // namespace StdString
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    template<typename ExpressionT>
+    inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) {
+        return Impl::Generic::Not<ExpressionT>( m );
+    }
+
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+
+    inline Impl::StdString::Equals      Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Equals( str, caseSensitivity );
+    }
+    inline Impl::StdString::Equals      Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity );
+    }
+    inline Impl::StdString::Contains    Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Contains( substr, caseSensitivity );
+    }
+    inline Impl::StdString::Contains    Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
+        return Impl::StdString::StartsWith( substr );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
+        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
+        return Impl::StdString::EndsWith( substr );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
+        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str("");
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition,
+                        char const* secondArg = "" );
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            m_stream.oss << value;
+            return *this;
+        }
+
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+        ResultBuilder& setLhs( std::string const& lhs );
+        ResultBuilder& setRhs( std::string const& rhs );
+        ResultBuilder& setOp( std::string const& op );
+
+        void endExpression();
+
+        std::string reconstructExpression() const;
+        AssertionResult build() const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void captureExpectedException( std::string const& expectedMessage );
+        void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher );
+        void handleResult( AssertionResult const& result );
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+        struct ExprComponents {
+            ExprComponents() : testFalse( false ) {}
+            bool testFalse;
+            std::string lhs, rhs, op;
+        } m_exprComponents;
+        CopyableStream m_stream;
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+    // So the compare overloads can be operator agnostic we convey the operator as a template
+    // enum, which is used to specialise an Evaluator for doing the comparison.
+    template<typename T1, typename T2, Operator Op>
+    class Evaluator{};
 
     template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs) {
-            return opCast( lhs ) ==  opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsNotEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) != opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsLessThan> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) < opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return opCast( lhs ) ==  opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) != opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) < opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsGreaterThan> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
             return opCast( lhs ) > opCast( rhs );
@@ -991,13 +1395,51 @@ namespace Internal {
         return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
     }
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+    // long long to unsigned X
+    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // unsigned long long to X
+    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+
+    // pointer to long long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
     // pointer to nullptr_t (when comparing against nullptr)
     template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
     }
     template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
     }
 #endif // CATCH_CONFIG_CPP11_NULLPTR
 
@@ -1095,6 +1537,11 @@ std::string toString( char value );
 std::string toString( signed char value );
 std::string toString( unsigned char value );
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
 std::string toString( std::nullptr_t );
 #endif
@@ -1107,7 +1554,7 @@ std::string toString( std::nullptr_t );
 
 namespace Detail {
 
-    extern std::string unprintableString;
+    extern const std::string unprintableString;
 
     struct BorgType {
         template<typename T> BorgType( T const& );
@@ -1190,7 +1637,7 @@ struct StringMaker<T*> {
     template<typename U>
     static std::string convert( U* p ) {
         if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
+            return "NULL";
         else
             return Detail::rawMemoryToString( p );
     }
@@ -1200,7 +1647,7 @@ template<typename R, typename C>
 struct StringMaker<R C::*> {
     static std::string convert( R C::* p ) {
         if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
+            return "NULL";
         else
             return Detail::rawMemoryToString( p );
     }
@@ -1472,6 +1919,7 @@ namespace Catch {
     class AssertionResult;
     struct AssertionInfo;
     struct SectionInfo;
+    struct SectionEndInfo;
     struct MessageInfo;
     class ScopedMessageBuilder;
     struct Counts;
@@ -1483,7 +1931,8 @@ namespace Catch {
         virtual void assertionEnded( AssertionResult const& result ) = 0;
         virtual bool sectionStarted(    SectionInfo const& sectionInfo,
                                         Counts& assertions ) = 0;
-        virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
         virtual void pushScopedMessage( MessageInfo const& message ) = 0;
         virtual void popScopedMessage( MessageInfo const& message ) = 0;
 
@@ -1604,16 +2053,16 @@ namespace Catch {
     } while( Catch::alwaysFalse() )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
         if( __catchResult.allowThrows() ) \
             try { \
                 expr; \
                 __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
             } \
             catch( ... ) { \
-                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+                __catchResult.captureExpectedException( matcher ); \
             } \
         else \
             __catchResult.captureResult( Catch::ResultWas::Ok ); \
@@ -1666,41 +2115,26 @@ namespace Catch {
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
         try { \
-            std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+            std::string matcherAsString = (matcher).toString(); \
             __catchResult \
                 .setLhs( Catch::toString( arg ) ) \
                 .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
                 .setOp( "matches" ) \
-                .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+                .setResultType( (matcher).match( arg ) ); \
             __catchResult.captureExpression(); \
         } catch( ... ) { \
             __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
         } \
         INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-// #included from: internal/catch_section.h
-#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
-
-// #included from: catch_section_info.h
-#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
-
-namespace Catch {
-
-    struct SectionInfo {
-        SectionInfo
-            (   SourceLineInfo const& _lineInfo,
-                std::string const& _name,
-                std::string const& _description = std::string() );
+    } while( Catch::alwaysFalse() )
 
-        std::string name;
-        std::string description;
-        SourceLineInfo lineInfo;
-    };
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
 
-} // end namespace Catch
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
 
 // #included from: catch_totals.hpp
 #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
@@ -1772,6 +2206,31 @@ namespace Catch {
     };
 }
 
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+        {}
+
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
 // #included from: catch_timer.h
 #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
 
@@ -2012,6 +2471,8 @@ using namespace Generators;
 #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
 
 #include <string>
+#include <vector>
+
 // #included from: catch_interfaces_registry_hub.h
 #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
 
@@ -2036,7 +2497,8 @@ namespace Catch {
 
     struct IMutableRegistryHub {
         virtual ~IMutableRegistryHub();
-        virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+        virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
         virtual void registerTest( TestCase const& testInfo ) = 0;
         virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
     };
@@ -2048,364 +2510,144 @@ namespace Catch {
 
 }
 
-
 namespace Catch {
 
     typedef std::string(*exceptionTranslateFunction)();
 
+    struct IExceptionTranslator;
+    typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
     struct IExceptionTranslator {
         virtual ~IExceptionTranslator();
-        virtual std::string translate() const = 0;
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
     };
 
     struct IExceptionTranslatorRegistry {
         virtual ~IExceptionTranslatorRegistry();
 
-        virtual std::string translateActiveException() const = 0;
-    };
-
-    class ExceptionTranslatorRegistrar {
-        template<typename T>
-        class ExceptionTranslator : public IExceptionTranslator {
-        public:
-
-            ExceptionTranslator( std::string(*translateFunction)( T& ) )
-            : m_translateFunction( translateFunction )
-            {}
-
-            virtual std::string translate() const {
-                try {
-                    throw;
-                }
-                catch( T& ex ) {
-                    return m_translateFunction( ex );
-                }
-            }
-
-        protected:
-            std::string(*m_translateFunction)( T& );
-        };
-
-    public:
-        template<typename T>
-        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
-            getMutableRegistryHub().registerTranslator
-                ( new ExceptionTranslator<T>( translateFunction ) );
-        }
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
-    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
-    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
-
-// #included from: internal/catch_approx.hpp
-#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
-
-#include <cmath>
-#include <limits>
-
-namespace Catch {
-namespace Detail {
-
-    class Approx {
-    public:
-        explicit Approx ( double value )
-        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
-            m_scale( 1.0 ),
-            m_value( value )
-        {}
-
-        Approx( Approx const& other )
-        :   m_epsilon( other.m_epsilon ),
-            m_scale( other.m_scale ),
-            m_value( other.m_value )
-        {}
-
-        static Approx custom() {
-            return Approx( 0 );
-        }
-
-        Approx operator()( double value ) {
-            Approx approx( value );
-            approx.epsilon( m_epsilon );
-            approx.scale( m_scale );
-            return approx;
-        }
-
-        friend bool operator == ( double lhs, Approx const& rhs ) {
-            // Thanks to Richard Harris for his help refining this formula
-            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
-        }
-
-        friend bool operator == ( Approx const& lhs, double rhs ) {
-            return operator==( rhs, lhs );
-        }
-
-        friend bool operator != ( double lhs, Approx const& rhs ) {
-            return !operator==( lhs, rhs );
-        }
-
-        friend bool operator != ( Approx const& lhs, double rhs ) {
-            return !operator==( rhs, lhs );
-        }
-
-        Approx& epsilon( double newEpsilon ) {
-            m_epsilon = newEpsilon;
-            return *this;
-        }
-
-        Approx& scale( double newScale ) {
-            m_scale = newScale;
-            return *this;
-        }
-
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << "Approx( " << Catch::toString( m_value ) << " )";
-            return oss.str();
-        }
-
-    private:
-        double m_epsilon;
-        double m_scale;
-        double m_value;
-    };
-}
-
-template<>
-inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
-    return value.toString();
-}
-
-} // end namespace Catch
-
-// #included from: internal/catch_matchers.hpp
-#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
-
-namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-    template<typename ExpressionT>
-    struct Matcher : SharedImpl<IShared>
-    {
-        typedef ExpressionT ExpressionType;
-
-        virtual ~Matcher() {}
-        virtual Ptr<Matcher> clone() const = 0;
-        virtual bool match( ExpressionT const& expr ) const = 0;
-        virtual std::string toString() const = 0;
-    };
-
-    template<typename DerivedT, typename ExpressionT>
-    struct MatcherImpl : Matcher<ExpressionT> {
-
-        virtual Ptr<Matcher<ExpressionT> > clone() const {
-            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
-        }
-    };
-
-    namespace Generic {
-
-        template<typename ExpressionT>
-        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AllOf() {}
-            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AllOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( !m_matchers[i]->match( expr ) )
-                        return false;
-                return true;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " and ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-        template<typename ExpressionT>
-        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AnyOf() {}
-            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( m_matchers[i]->match( expr ) )
-                        return true;
-                return false;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " or ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-    }
-
-    namespace StdString {
-
-        inline std::string makeString( std::string const& str ) { return str; }
-        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+        virtual std::string translateActiveException() const = 0;
+    };
 
-        struct Equals : MatcherImpl<Equals, std::string> {
-            Equals( std::string const& str ) : m_str( str ){}
-            Equals( Equals const& other ) : m_str( other.m_str ){}
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
 
-            virtual ~Equals();
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
 
-            virtual bool match( std::string const& expr ) const {
-                return m_str == expr;
-            }
-            virtual std::string toString() const {
-                return "equals: \"" + m_str + "\"";
+            virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+                try {
+                    if( it == itEnd )
+                        throw;
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
             }
 
-            std::string m_str;
+        protected:
+            std::string(*m_translateFunction)( T& );
         };
 
-        struct Contains : MatcherImpl<Contains, std::string> {
-            Contains( std::string const& substr ) : m_substr( substr ){}
-            Contains( Contains const& other ) : m_substr( other.m_substr ){}
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
 
-            virtual ~Contains();
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) != std::string::npos;
-            }
-            virtual std::string toString() const {
-                return "contains: \"" + m_substr + "\"";
-            }
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
 
-            std::string m_substr;
-        };
+#include <cmath>
+#include <limits>
 
-        struct StartsWith : MatcherImpl<StartsWith, std::string> {
-            StartsWith( std::string const& substr ) : m_substr( substr ){}
-            StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+namespace Catch {
+namespace Detail {
 
-            virtual ~StartsWith();
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == 0;
-            }
-            virtual std::string toString() const {
-                return "starts with: \"" + m_substr + "\"";
-            }
+        Approx( Approx const& other )
+        :   m_epsilon( other.m_epsilon ),
+            m_scale( other.m_scale ),
+            m_value( other.m_value )
+        {}
 
-            std::string m_substr;
-        };
+        static Approx custom() {
+            return Approx( 0 );
+        }
 
-        struct EndsWith : MatcherImpl<EndsWith, std::string> {
-            EndsWith( std::string const& substr ) : m_substr( substr ){}
-            EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.scale( m_scale );
+            return approx;
+        }
 
-            virtual ~EndsWith();
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+        }
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == expr.size() - m_substr.size();
-            }
-            virtual std::string toString() const {
-                return "ends with: \"" + m_substr + "\"";
-            }
+        friend bool operator == ( Approx const& lhs, double rhs ) {
+            return operator==( rhs, lhs );
+        }
 
-            std::string m_substr;
-        };
-    } // namespace StdString
-    } // namespace Impl
+        friend bool operator != ( double lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
 
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
+        friend bool operator != ( Approx const& lhs, double rhs ) {
+            return !operator==( rhs, lhs );
+        }
 
-    inline Impl::StdString::Equals      Equals( std::string const& str ) {
-        return Impl::StdString::Equals( str );
-    }
-    inline Impl::StdString::Equals      Equals( const char* str ) {
-        return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
-    }
-    inline Impl::StdString::Contains    Contains( std::string const& substr ) {
-        return Impl::StdString::Contains( substr );
-    }
-    inline Impl::StdString::Contains    Contains( const char* substr ) {
-        return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
-        return Impl::StdString::StartsWith( substr );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
-        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
-        return Impl::StdString::EndsWith( substr );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
-        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
-    }
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
 
-} // namespace Matchers
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
 
-using namespace Matchers;
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
 
-} // namespace Catch
+    private:
+        double m_epsilon;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
 
 // #included from: internal/catch_interfaces_tag_alias_registry.h
 #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
@@ -2440,12 +2682,12 @@ namespace Catch {
     template<typename T>
     class Option {
     public:
-        Option() : nullableValue( NULL ) {}
+        Option() : nullableValue( CATCH_NULL ) {}
         Option( T const& _value )
         : nullableValue( new( storage ) T( _value ) )
         {}
         Option( Option const& _other )
-        : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+        : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
         {}
 
         ~Option() {
@@ -2469,7 +2711,7 @@ namespace Catch {
         void reset() {
             if( nullableValue )
                 nullableValue->~T();
-            nullableValue = NULL;
+            nullableValue = CATCH_NULL;
         }
 
         T& operator*() { return *nullableValue; }
@@ -2481,10 +2723,10 @@ namespace Catch {
             return nullableValue ? *nullableValue : defaultValue;
         }
 
-        bool some() const { return nullableValue != NULL; }
-        bool none() const { return nullableValue == NULL; }
+        bool some() const { return nullableValue != CATCH_NULL; }
+        bool none() const { return nullableValue == CATCH_NULL; }
 
-        bool operator !() const { return nullableValue == NULL; }
+        bool operator !() const { return nullableValue == CATCH_NULL; }
         operator SafeBool::type() const {
             return SafeBool::makeSafe( some() );
         }
@@ -2542,6 +2784,8 @@ namespace Catch {
 
         TestCaseInfo( TestCaseInfo const& other );
 
+        friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
         bool isHidden() const;
         bool throws() const;
         bool okToFail() const;
@@ -2654,7 +2898,7 @@ namespace Catch {
 
     inline size_t registerTestMethods() {
         size_t noTestMethods = 0;
-        int noClasses = objc_getClassList( NULL, 0 );
+        int noClasses = objc_getClassList( CATCH_NULL, 0 );
 
         Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
         objc_getClassList( classes, noClasses );
@@ -2796,7 +3040,7 @@ return @ desc; \
 #pragma clang diagnostic ignored "-Wweak-vtables"
 #endif
 
-// #included from: ../catch_runner.hpp
+// #included from: ../catch_session.hpp
 #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
 
 // #included from: internal/catch_commandline.hpp
@@ -2821,6 +3065,67 @@ return @ desc; \
 #pragma clang diagnostic ignored "-Wpadded"
 #endif
 
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_wildcard( NoWildcard ),
+            m_pattern( adjustCase( pattern ) )
+        {
+            if( startsWith( m_pattern, "*" ) ) {
+                m_pattern = m_pattern.substr( 1 );
+                m_wildcard = WildcardAtStart;
+            }
+            if( endsWith( m_pattern, "*" ) ) {
+                m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+                m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+            }
+        }
+        virtual ~WildcardPattern();
+        virtual bool matches( std::string const& str ) const {
+            switch( m_wildcard ) {
+                case NoWildcard:
+                    return m_pattern == adjustCase( str );
+                case WildcardAtStart:
+                    return endsWith( adjustCase( str ), m_pattern );
+                case WildcardAtEnd:
+                    return startsWith( adjustCase( str ), m_pattern );
+                case WildcardAtBothEnds:
+                    return contains( adjustCase( str ), m_pattern );
+            }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+            throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+        }
+    private:
+        std::string adjustCase( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+        }
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard;
+        std::string m_pattern;
+    };
+}
+
 #include <string>
 #include <vector>
 
@@ -2832,50 +3137,18 @@ namespace Catch {
             virtual bool matches( TestCaseInfo const& testCase ) const = 0;
         };
         class NamePattern : public Pattern {
-            enum WildcardPosition {
-                NoWildcard = 0,
-                WildcardAtStart = 1,
-                WildcardAtEnd = 2,
-                WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
-            };
-
         public:
-            NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
-                if( startsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 1 );
-                    m_wildcard = WildcardAtStart;
-                }
-                if( endsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 0, m_name.size()-1 );
-                    m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
-                }
-            }
+            NamePattern( std::string const& name )
+            : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+            {}
             virtual ~NamePattern();
             virtual bool matches( TestCaseInfo const& testCase ) const {
-                switch( m_wildcard ) {
-                    case NoWildcard:
-                        return m_name == toLower( testCase.name );
-                    case WildcardAtStart:
-                        return endsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtEnd:
-                        return startsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtBothEnds:
-                        return contains( toLower( testCase.name ), m_name );
-                }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-                throw std::logic_error( "Unknown enum" );
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+                return m_wildcardPattern.matches( toLower( testCase.name ) );
             }
         private:
-            std::string m_name;
-            WildcardPosition m_wildcard;
+            WildcardPattern m_wildcardPattern;
         };
+
         class TagPattern : public Pattern {
         public:
             TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
@@ -2886,6 +3159,7 @@ namespace Catch {
         private:
             std::string m_tag;
         };
+
         class ExcludedPattern : public Pattern {
         public:
             ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
@@ -3083,28 +3357,62 @@ namespace Catch {
 // #included from: catch_stream.h
 #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
 
-#include <streambuf>
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
 
-#ifdef __clang__
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
+#include <streambuf>
 
 namespace Catch {
 
-    class Stream {
+    class StreamBufBase : public std::streambuf {
     public:
-        Stream();
-        Stream( std::streambuf* _streamBuf, bool _isOwned );
-        void release();
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
 
-        std::streambuf* streamBuf;
+#include <streambuf>
+#include <ostream>
+#include <fstream>
 
-    private:
-        bool isOwned;
-    };
+namespace Catch {
 
     std::ostream& cout();
     std::ostream& cerr();
+
+    struct IStream {
+        virtual ~IStream() CATCH_NOEXCEPT;
+        virtual std::ostream& stream() const = 0;
+    };
+
+    class FileStream : public IStream {
+        mutable std::ofstream m_ofs;
+    public:
+        FileStream( std::string const& filename );
+        virtual ~FileStream() CATCH_NOEXCEPT;
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class CoutStream : public IStream {
+        mutable std::ostream m_os;
+    public:
+        CoutStream();
+        virtual ~CoutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class DebugOutStream : public IStream {
+        std::auto_ptr<StreamBufBase> m_streamBuf;
+        mutable std::ostream m_os;
+    public:
+        DebugOutStream();
+        virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
 }
 
 #include <memory>
@@ -3132,6 +3440,7 @@ namespace Catch {
             showHelp( false ),
             showInvisibles( false ),
             forceColour( false ),
+            filenamesAsTags( false ),
             abortAfter( -1 ),
             rngSeed( 0 ),
             verbosity( Verbosity::Normal ),
@@ -3151,6 +3460,7 @@ namespace Catch {
         bool showHelp;
         bool showInvisibles;
         bool forceColour;
+        bool filenamesAsTags;
 
         int abortAfter;
         unsigned int rngSeed;
@@ -3160,11 +3470,11 @@ namespace Catch {
         ShowDurations::OrNot showDurations;
         RunTests::InWhatOrder runOrder;
 
-        std::string reporterName;
         std::string outputFilename;
         std::string name;
         std::string processName;
 
+        std::vector<std::string> reporterNames;
         std::vector<std::string> testsOrTags;
     };
 
@@ -3176,12 +3486,11 @@ namespace Catch {
     public:
 
         Config()
-        :   m_os( Catch::cout().rdbuf() )
         {}
 
         Config( ConfigData const& data )
         :   m_data( data ),
-            m_os( Catch::cout().rdbuf() )
+            m_stream( openStream() )
         {
             if( !data.testsOrTags.empty() ) {
                 TestSpecParser parser( ITagAliasRegistry::get() );
@@ -3192,12 +3501,6 @@ namespace Catch {
         }
 
         virtual ~Config() {
-            m_os.rdbuf( Catch::cout().rdbuf() );
-            m_stream.release();
-        }
-
-        void setFilename( std::string const& filename ) {
-            m_data.outputFilename = filename;
         }
 
         std::string const& getFilename() const {
@@ -3213,18 +3516,7 @@ namespace Catch {
 
         bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
 
-        void setStreamBuf( std::streambuf* buf ) {
-            m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
-        }
-
-        void useStream( std::string const& streamName ) {
-            Stream stream = createStream( streamName );
-            setStreamBuf( stream.streamBuf );
-            m_stream.release();
-            m_stream = stream;
-        }
-
-        std::string getReporterName() const { return m_data.reporterName; }
+        std::vector<std::string> getReporterNames() const { return m_data.reporterNames; }
 
         int abortAfter() const { return m_data.abortAfter; }
 
@@ -3235,7 +3527,7 @@ namespace Catch {
 
         // IConfig interface
         virtual bool allowThrows() const        { return !m_data.noThrow; }
-        virtual std::ostream& stream() const    { return m_os; }
+        virtual std::ostream& stream() const    { return m_stream->stream(); }
         virtual std::string name() const        { return m_data.name.empty() ? m_data.processName : m_data.name; }
         virtual bool includeSuccessfulResults() const   { return m_data.showSuccessfulTests; }
         virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
@@ -3245,10 +3537,22 @@ namespace Catch {
         virtual bool forceColour() const { return m_data.forceColour; }
 
     private:
+
+        IStream const* openStream() {
+            if( m_data.outputFilename.empty() )
+                return new CoutStream();
+            else if( m_data.outputFilename[0] == '%' ) {
+                if( m_data.outputFilename == "%debug" )
+                    return new DebugOutStream();
+                else
+                    throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+            }
+            else
+                return new FileStream( m_data.outputFilename );
+        }
         ConfigData m_data;
 
-        Stream m_stream;
-        mutable std::ostream m_os;
+        std::auto_ptr<IStream const> m_stream;
         TestSpec m_testSpec;
     };
 
@@ -3517,11 +3821,11 @@ namespace Clara {
         template<typename ConfigT>
         class BoundArgFunction {
         public:
-            BoundArgFunction() : functionObj( NULL ) {}
+            BoundArgFunction() : functionObj( CATCH_NULL ) {}
             BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
-            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {}
             BoundArgFunction& operator = ( BoundArgFunction const& other ) {
-                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL;
                 delete functionObj;
                 functionObj = newFunctionObj;
                 return *this;
@@ -3537,7 +3841,7 @@ namespace Clara {
             bool takesArg() const { return functionObj->takesArg(); }
 
             bool isSet() const {
-                return functionObj != NULL;
+                return functionObj != CATCH_NULL;
             }
         private:
             IArgFunction<ConfigT>* functionObj;
@@ -3755,12 +4059,7 @@ namespace Clara {
             }
         };
 
-        // NOTE: std::auto_ptr is deprecated in c++11/c++0x
-#if defined(__cplusplus) && __cplusplus > 199711L
-        typedef std::unique_ptr<Arg> ArgAutoPtr;
-#else
-        typedef std::auto_ptr<Arg> ArgAutoPtr;
-#endif
+        typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr;
 
         friend void addOptName( Arg& arg, std::string const& optName )
         {
@@ -3836,8 +4135,8 @@ namespace Clara {
                 m_arg->description = description;
                 return *this;
             }
-            ArgBuilder& detail( std::string const& detail ) {
-                m_arg->detail = detail;
+            ArgBuilder& detail( std::string const& _detail ) {
+                m_arg->detail = _detail;
                 return *this;
             }
 
@@ -3920,14 +4219,14 @@ namespace Clara {
                 maxWidth = (std::max)( maxWidth, it->commands().size() );
 
             for( it = itBegin; it != itEnd; ++it ) {
-                Detail::Text usage( it->commands(), Detail::TextAttributes()
+                Detail::Text usageText( it->commands(), Detail::TextAttributes()
                                                         .setWidth( maxWidth+indent )
                                                         .setIndent( indent ) );
                 Detail::Text desc( it->description, Detail::TextAttributes()
                                                         .setWidth( width - maxWidth - 3 ) );
 
-                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
-                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usageText.size() ? usageText[i] : "";
                     os << usageCol;
 
                     if( i < desc.size() && !desc[i].empty() )
@@ -4133,6 +4432,7 @@ namespace Catch {
         config.abortAfter = x;
     }
     inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+    inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
 
     inline void addWarning( ConfigData& config, std::string const& _warning ) {
         if( _warning == "NoAssertions" )
@@ -4226,7 +4526,7 @@ namespace Catch {
         cli["-r"]["--reporter"]
 //            .placeholder( "name[:filename]" )
             .describe( "reporter to use (defaults to console)" )
-            .bind( &ConfigData::reporterName, "name" );
+            .bind( &addReporterName, "name" );
 
         cli["-n"]["--name"]
             .describe( "suite name" )
@@ -4263,6 +4563,10 @@ namespace Catch {
             .describe( "load test names to run from a file" )
             .bind( &loadTestNamesFromFile, "filename" );
 
+        cli["-#"]["--filenames-as-tags"]
+            .describe( "adds a tag for the filename" )
+            .bind( &ConfigData::filenamesAsTags );
+
         // Less common commands which don't have a short form
         cli["--list-test-names-only"]
             .describe( "list all/matching test cases names only" )
@@ -4520,18 +4824,18 @@ namespace Catch {
 namespace Catch
 {
     struct ReporterConfig {
-        explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+        explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
         :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
 
-        ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+        ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
         :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
 
         std::ostream& stream() const    { return *m_stream; }
-        Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+        Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
 
     private:
         std::ostream* m_stream;
-        Ptr<IConfig> m_fullConfig;
+        Ptr<IConfig const> m_fullConfig;
     };
 
     struct ReporterPreferences {
@@ -4733,6 +5037,7 @@ namespace Catch
 
         // The return value indicates if the messages buffer should be cleared:
         virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
         virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
         virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
         virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
@@ -4741,20 +5046,24 @@ namespace Catch
         virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
     };
 
-    struct IReporterFactory {
+    struct IReporterFactory : IShared {
         virtual ~IReporterFactory();
         virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
         virtual std::string getDescription() const = 0;
     };
 
     struct IReporterRegistry {
-        typedef std::map<std::string, IReporterFactory*> FactoryMap;
+        typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+        typedef std::vector<Ptr<IReporterFactory> > Listeners;
 
         virtual ~IReporterRegistry();
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
         virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
     };
 
+    Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
 }
 
 #include <limits>
@@ -4777,8 +5086,7 @@ namespace Catch {
         nameAttr.setInitialIndent( 2 ).setIndent( 4 );
         tagsAttr.setIndent( 6 );
 
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4806,8 +5114,7 @@ namespace Catch {
         if( !config.testSpec().hasFilters() )
             testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
         std::size_t matchedTests = 0;
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4847,8 +5154,7 @@ namespace Catch {
 
         std::map<std::string, TagInfo> tagCounts;
 
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4919,7 +5225,7 @@ namespace Catch {
 
 } // end namespace Catch
 
-// #included from: internal/catch_runner_impl.hpp
+// #included from: internal/catch_run_context.hpp
 #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
 
 // #included from: catch_test_case_tracker.hpp
@@ -4928,137 +5234,300 @@ namespace Catch {
 #include <map>
 #include <string>
 #include <assert.h>
+#include <vector>
 
 namespace Catch {
-namespace SectionTracking {
+namespace TestCaseTracking {
 
-    class TrackedSection {
+    struct ITracker : SharedImpl<> {
+        virtual ~ITracker();
 
-        typedef std::map<std::string, TrackedSection> TrackedSections;
+        // static queries
+        virtual std::string name() const = 0;
+
+        // dynamic queries
+        virtual bool isComplete() const = 0; // Successfully completed or failed
+        virtual bool isSuccessfullyCompleted() const = 0;
+        virtual bool isOpen() const = 0; // Started but not complete
+        virtual bool hasChildren() const = 0;
+
+        virtual ITracker& parent() = 0;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( Ptr<ITracker> const& child ) = 0;
+        virtual ITracker* findChild( std::string const& name ) = 0;
+        virtual void openChild() = 0;
+    };
+
+    class TrackerContext {
 
-    public:
         enum RunState {
             NotStarted,
             Executing,
-            ExecutingChildren,
-            Completed
+            CompletedCycle
         };
 
-        TrackedSection( std::string const& name, TrackedSection* parent )
-        :   m_name( name ), m_runState( NotStarted ), m_parent( parent )
-        {}
+        Ptr<ITracker> m_rootTracker;
+        ITracker* m_currentTracker;
+        RunState m_runState;
+
+    public:
 
-        RunState runState() const { return m_runState; }
+        static TrackerContext& instance() {
+            static TrackerContext s_instance;
+            return s_instance;
+        }
 
-        TrackedSection* findChild( std::string const& childName );
-        TrackedSection* acquireChild( std::string const& childName );
+        TrackerContext()
+        :   m_currentTracker( CATCH_NULL ),
+            m_runState( NotStarted )
+        {}
 
-        void enter() {
-            if( m_runState == NotStarted )
-                m_runState = Executing;
+        ITracker& startRun();
+
+        void endRun() {
+            m_rootTracker.reset();
+            m_currentTracker = CATCH_NULL;
+            m_runState = NotStarted;
         }
-        void leave();
 
-        TrackedSection* getParent() {
-            return m_parent;
+        void startCycle() {
+            m_currentTracker = m_rootTracker.get();
+            m_runState = Executing;
         }
-        bool hasChildren() const {
-            return !m_children.empty();
+        void completeCycle() {
+            m_runState = CompletedCycle;
         }
 
-    private:
-        std::string m_name;
-        RunState m_runState;
-        TrackedSections m_children;
-        TrackedSection* m_parent;
+        bool completedCycle() const {
+            return m_runState == CompletedCycle;
+        }
+        ITracker& currentTracker() {
+            return *m_currentTracker;
+        }
+        void setCurrentTracker( ITracker* tracker ) {
+            m_currentTracker = tracker;
+        }
     };
 
-    inline TrackedSection* TrackedSection::findChild( std::string const& childName ) {
-        TrackedSections::iterator it = m_children.find( childName );
-        return it != m_children.end()
-            ? &it->second
-            : NULL;
-    }
-    inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) {
-        if( TrackedSection* child = findChild( childName ) )
-            return child;
-        m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
-        return findChild( childName );
-    }
-    inline void TrackedSection::leave() {
-        for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
-                it != itEnd;
-                ++it )
-            if( it->second.runState() != Completed ) {
-                m_runState = ExecutingChildren;
-                return;
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+        class TrackerHasName {
+            std::string m_name;
+        public:
+            TrackerHasName( std::string const& name ) : m_name( name ) {}
+            bool operator ()( Ptr<ITracker> const& tracker ) {
+                return tracker->name() == m_name;
             }
-        m_runState = Completed;
-    }
-
-    class TestCaseTracker {
+        };
+        typedef std::vector<Ptr<ITracker> > Children;
+        std::string m_name;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState;
     public:
-        TestCaseTracker( std::string const& testCaseName )
-        :   m_testCase( testCaseName, NULL ),
-            m_currentSection( &m_testCase ),
-            m_completedASectionThisRun( false )
+        TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent )
+        :   m_name( name ),
+            m_ctx( ctx ),
+            m_parent( parent ),
+            m_runState( NotStarted )
         {}
+        virtual ~TrackerBase();
 
-        bool enterSection( std::string const& name ) {
-            TrackedSection* child = m_currentSection->acquireChild( name );
-            if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
-                return false;
+        virtual std::string name() const CATCH_OVERRIDE {
+            return m_name;
+        }
+        virtual bool isComplete() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully || m_runState == Failed;
+        }
+        virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully;
+        }
+        virtual bool isOpen() const CATCH_OVERRIDE {
+            return m_runState != NotStarted && !isComplete();
+        }
+        virtual bool hasChildren() const CATCH_OVERRIDE {
+            return !m_children.empty();
+        }
 
-            m_currentSection = child;
-            m_currentSection->enter();
-            return true;
+        virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+            m_children.push_back( child );
+        }
+
+        virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE {
+            Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) );
+            return( it != m_children.end() )
+                ? it->get()
+                : CATCH_NULL;
         }
-        void leaveSection() {
-            m_currentSection->leave();
-            m_currentSection = m_currentSection->getParent();
-            assert( m_currentSection != NULL );
-            m_completedASectionThisRun = true;
+        virtual ITracker& parent() CATCH_OVERRIDE {
+            assert( m_parent ); // Should always be non-null except for root
+            return *m_parent;
         }
 
-        bool currentSectionHasChildren() const {
-            return m_currentSection->hasChildren();
+        virtual void openChild() CATCH_OVERRIDE {
+            if( m_runState != ExecutingChildren ) {
+                m_runState = ExecutingChildren;
+                if( m_parent )
+                    m_parent->openChild();
+            }
         }
-        bool isCompleted() const {
-            return m_testCase.runState() == TrackedSection::Completed;
+        void open() {
+            m_runState = Executing;
+            moveToThis();
+            if( m_parent )
+                m_parent->openChild();
         }
 
-        class Guard {
-        public:
-            Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
-                m_tracker.enterTestCase();
+        virtual void close() CATCH_OVERRIDE {
+
+            // Close any still open children (e.g. generators)
+            while( &m_ctx.currentTracker() != this )
+                m_ctx.currentTracker().close();
+
+            switch( m_runState ) {
+                case NotStarted:
+                case CompletedSuccessfully:
+                case Failed:
+                    throw std::logic_error( "Illogical state" );
+
+                case NeedsAnotherRun:
+                    break;;
+
+                case Executing:
+                    m_runState = CompletedSuccessfully;
+                    break;
+                case ExecutingChildren:
+                    if( m_children.empty() || m_children.back()->isComplete() )
+                        m_runState = CompletedSuccessfully;
+                    break;
+
+                default:
+                    throw std::logic_error( "Unexpected state" );
+            }
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void fail() CATCH_OVERRIDE {
+            m_runState = Failed;
+            if( m_parent )
+                m_parent->markAsNeedingAnotherRun();
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+            m_runState = NeedsAnotherRun;
+        }
+    private:
+        void moveToParent() {
+            assert( m_parent );
+            m_ctx.setCurrentTracker( m_parent );
+        }
+        void moveToThis() {
+            m_ctx.setCurrentTracker( this );
+        }
+    };
+
+    class SectionTracker : public TrackerBase {
+    public:
+        SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent )
+        :   TrackerBase( name, ctx, parent )
+        {}
+        virtual ~SectionTracker();
+
+        static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) {
+            SectionTracker* section = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+                section = dynamic_cast<SectionTracker*>( childTracker );
+                assert( section );
             }
-            ~Guard() {
-                m_tracker.leaveTestCase();
+            else {
+                section = new SectionTracker( name, ctx, &currentTracker );
+                currentTracker.addChild( section );
             }
-        private:
-            Guard( Guard const& );
-            void operator = ( Guard const& );
-            TestCaseTracker& m_tracker;
-        };
+            if( !ctx.completedCycle() && !section->isComplete() ) {
 
-    private:
-        void enterTestCase() {
-            m_currentSection = &m_testCase;
-            m_completedASectionThisRun = false;
-            m_testCase.enter();
+                section->open();
+            }
+            return *section;
+        }
+    };
+
+    class IndexTracker : public TrackerBase {
+        int m_size;
+        int m_index;
+    public:
+        IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size )
+        :   TrackerBase( name, ctx, parent ),
+            m_size( size ),
+            m_index( -1 )
+        {}
+        virtual ~IndexTracker();
+
+        static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
+            IndexTracker* tracker = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+                tracker = dynamic_cast<IndexTracker*>( childTracker );
+                assert( tracker );
+            }
+            else {
+                tracker = new IndexTracker( name, ctx, &currentTracker, size );
+                currentTracker.addChild( tracker );
+            }
+
+            if( !ctx.completedCycle() && !tracker->isComplete() ) {
+                if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+                    tracker->moveNext();
+                tracker->open();
+            }
+
+            return *tracker;
         }
-        void leaveTestCase() {
-            m_testCase.leave();
+
+        int index() const { return m_index; }
+
+        void moveNext() {
+            m_index++;
+            m_children.clear();
         }
 
-        TrackedSection m_testCase;
-        TrackedSection* m_currentSection;
-        bool m_completedASectionThisRun;
+        virtual void close() CATCH_OVERRIDE {
+            TrackerBase::close();
+            if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+                m_runState = Executing;
+        }
     };
 
-} // namespace SectionTracking
+    inline ITracker& TrackerContext::startRun() {
+        m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL );
+        m_currentTracker = CATCH_NULL;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+} // namespace TestCaseTracking
 
-using SectionTracking::TestCaseTracker;
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
 
 } // namespace Catch
 
@@ -5174,15 +5643,12 @@ namespace Catch {
 
     public:
 
-        explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
-        :   m_runInfo( config->name() ),
+        explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( _config->name() ),
             m_context( getCurrentMutableContext() ),
-            m_activeTestCase( NULL ),
-            m_config( config ),
-            m_reporter( reporter ),
-            m_prevRunner( m_context.getRunner() ),
-            m_prevResultCapture( m_context.getResultCapture() ),
-            m_prevConfig( m_context.getConfig() )
+            m_activeTestCase( CATCH_NULL ),
+            m_config( _config ),
+            m_reporter( reporter )
         {
             m_context.setRunner( this );
             m_context.setConfig( m_config );
@@ -5192,10 +5658,6 @@ namespace Catch {
 
         virtual ~RunContext() {
             m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
-            m_context.setRunner( m_prevRunner );
-            m_context.setConfig( NULL );
-            m_context.setResultCapture( m_prevResultCapture );
-            m_context.setConfig( m_prevConfig );
         }
 
         void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
@@ -5216,14 +5678,17 @@ namespace Catch {
             m_reporter->testCaseStarting( testInfo );
 
             m_activeTestCase = &testCase;
-            m_testCaseTracker = TestCaseTracker( testInfo.name );
 
             do {
+                m_trackerContext.startRun();
                 do {
+                    m_trackerContext.startCycle();
+                    m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name );
                     runCurrentTest( redirectedCout, redirectedCerr );
                 }
-                while( !m_testCaseTracker->isCompleted() && !aborting() );
+                while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
             }
+            // !TBD: deprecated - this will be replaced by indexed trackers
             while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
 
             Totals deltaTotals = m_totals.delta( prevTotals );
@@ -5234,8 +5699,8 @@ namespace Catch {
                                                         redirectedCerr,
                                                         aborting() ) );
 
-            m_activeTestCase = NULL;
-            m_testCaseTracker.reset();
+            m_activeTestCase = CATCH_NULL;
+            m_testCaseTracker = CATCH_NULL;
 
             return deltaTotals;
         }
@@ -5270,8 +5735,10 @@ namespace Catch {
             std::ostringstream oss;
             oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
 
-            if( !m_testCaseTracker->enterSection( oss.str() ) )
+            ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() );
+            if( !sectionTracker.isOpen() )
                 return false;
+            m_activeSections.push_back( &sectionTracker );
 
             m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
 
@@ -5282,30 +5749,40 @@ namespace Catch {
             return true;
         }
         bool testForMissingAssertions( Counts& assertions ) {
-            if( assertions.total() != 0 ||
-                    !m_config->warnAboutMissingAssertions() ||
-                    m_testCaseTracker->currentSectionHasChildren() )
+            if( assertions.total() != 0 )
+                return false;
+            if( !m_config->warnAboutMissingAssertions() )
+                return false;
+            if( m_trackerContext.currentTracker().hasChildren() )
                 return false;
             m_totals.assertions.failed++;
             assertions.failed++;
             return true;
         }
 
-        virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
-            if( std::uncaught_exception() ) {
-                m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
-                return;
-            }
-
-            Counts assertions = m_totals.assertions - prevAssertions;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+            Counts assertions = m_totals.assertions - endInfo.prevAssertions;
             bool missingAssertions = testForMissingAssertions( assertions );
 
-            m_testCaseTracker->leaveSection();
+            if( !m_activeSections.empty() ) {
+                m_activeSections.back()->close();
+                m_activeSections.pop_back();
+            }
 
-            m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+            m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
             m_messages.clear();
         }
 
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+            if( m_unfinishedSections.empty() )
+                m_activeSections.back()->fail();
+            else
+                m_activeSections.back()->close();
+            m_activeSections.pop_back();
+
+            m_unfinishedSections.push_back( endInfo );
+        }
+
         virtual void pushScopedMessage( MessageInfo const& message ) {
             m_messages.push_back( message );
         }
@@ -5371,7 +5848,8 @@ namespace Catch {
             double duration = 0;
             try {
                 m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
-                TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+                seedRng( *m_config );
 
                 Timer timer;
                 timer.start();
@@ -5391,6 +5869,7 @@ namespace Catch {
             catch(...) {
                 makeUnexpectedResultBuilder().useActiveException();
             }
+            m_testCaseTracker->close();
             handleUnfinishedSections();
             m_messages.clear();
 
@@ -5425,39 +5904,29 @@ namespace Catch {
         void handleUnfinishedSections() {
             // If sections ended prematurely due to an exception we stored their
             // infos here so we can tear them down outside the unwind process.
-            for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+            for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
                         itEnd = m_unfinishedSections.rend();
                     it != itEnd;
                     ++it )
-                sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+                sectionEnded( *it );
             m_unfinishedSections.clear();
         }
 
-        struct UnfinishedSections {
-            UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
-            : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
-            {}
-
-            SectionInfo info;
-            Counts prevAssertions;
-            double durationInSeconds;
-        };
-
         TestRunInfo m_runInfo;
         IMutableContext& m_context;
         TestCase const* m_activeTestCase;
-        Option<TestCaseTracker> m_testCaseTracker;
+        ITracker* m_testCaseTracker;
+        ITracker* m_currentSectionTracker;
         AssertionResult m_lastResult;
 
         Ptr<IConfig const> m_config;
         Totals m_totals;
         Ptr<IStreamingReporter> m_reporter;
         std::vector<MessageInfo> m_messages;
-        IRunner* m_prevRunner;
-        IResultCapture* m_prevResultCapture;
-        Ptr<IConfig const> m_prevConfig;
         AssertionInfo m_lastAssertionInfo;
-        std::vector<UnfinishedSections> m_unfinishedSections;
+        std::vector<SectionEndInfo> m_unfinishedSections;
+        std::vector<ITracker*> m_activeSections;
+        TrackerContext m_trackerContext;
     };
 
     IResultCapture& getResultCapture() {
@@ -5505,89 +5974,87 @@ namespace Catch {
 
 namespace Catch {
 
-    class Runner {
-
-    public:
-        Runner( Ptr<Config> const& config )
-        :   m_config( config )
-        {
-            openStream();
-            makeReporter();
+    Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+        Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+        if( !reporter ) {
+            std::ostringstream oss;
+            oss << "No reporter registered with name: '" << reporterName << "'";
+            throw std::domain_error( oss.str() );
         }
+        return reporter;
+    }
 
-        Totals runTests() {
-
-            RunContext context( m_config.get(), m_reporter );
+    Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+        std::vector<std::string> reporters = config->getReporterNames();
+        if( reporters.empty() )
+            reporters.push_back( "console" );
 
-            Totals totals;
+        Ptr<IStreamingReporter> reporter;
+        for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+                it != itEnd;
+                ++it )
+            reporter = addReporter( reporter, createReporter( *it, config ) );
+        return reporter;
+    }
+    Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+        IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+        for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+                it != itEnd;
+                ++it )
+            reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+        return reporters;
+    }
 
-            context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
+    Totals runTests( Ptr<Config> const& config ) {
 
-            TestSpec testSpec = m_config->testSpec();
-            if( !testSpec.hasFilters() )
-                testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+        Ptr<IConfig const> iconfig = config.get();
 
-            std::vector<TestCase> testCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+        Ptr<IStreamingReporter> reporter = makeReporter( config );
+        reporter = addListeners( iconfig, reporter );
 
-            int testsRunForGroup = 0;
-            for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
-                    it != itEnd;
-                    ++it ) {
-                testsRunForGroup++;
-                if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+        RunContext context( iconfig, reporter );
 
-                    if( context.aborting() )
-                        break;
+        Totals totals;
 
-                    totals += context.runTest( *it );
-                    m_testsAlreadyRun.insert( *it );
-                }
-            }
-            std::vector<TestCase> skippedTestCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
+        context.testGroupStarting( config->name(), 1, 1 );
 
-            for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
-                    it != itEnd;
-                    ++it )
-                m_reporter->skipTest( *it );
+        TestSpec testSpec = config->testSpec();
+        if( !testSpec.hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
 
-            context.testGroupEnded( "all tests", totals, 1, 1 );
-            return totals;
+        std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+        for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+                it != itEnd;
+                ++it ) {
+            if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+                totals += context.runTest( *it );
+            else
+                reporter->skipTest( *it );
         }
 
-    private:
-        void openStream() {
-            // Open output file, if specified
-            if( !m_config->getFilename().empty() ) {
-                m_ofs.open( m_config->getFilename().c_str() );
-                if( m_ofs.fail() ) {
-                    std::ostringstream oss;
-                    oss << "Unable to open file: '" << m_config->getFilename() << "'";
-                    throw std::domain_error( oss.str() );
-                }
-                m_config->setStreamBuf( m_ofs.rdbuf() );
-            }
-        }
-        void makeReporter() {
-            std::string reporterName = m_config->getReporterName().empty()
-                ? "console"
-                : m_config->getReporterName();
+        context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+        return totals;
+    }
 
-            m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
-            if( !m_reporter ) {
-                std::ostringstream oss;
-                oss << "No reporter registered with name: '" << reporterName << "'";
-                throw std::domain_error( oss.str() );
-            }
-        }
+    void applyFilenamesAsTags( IConfig const& config ) {
+        std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+        for(std::size_t i = 0; i < tests.size(); ++i ) {
+            TestCase& test = const_cast<TestCase&>( tests[i] );
+            std::set<std::string> tags = test.tags;
 
-    private:
-        Ptr<Config> m_config;
-        std::ofstream m_ofs;
-        Ptr<IStreamingReporter> m_reporter;
-        std::set<TestCase> m_testsAlreadyRun;
-    };
+            std::string filename = test.lineInfo.file;
+            std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+            if( lastSlash != std::string::npos )
+                filename = filename.substr( lastSlash+1 );
+
+            std::string::size_type lastDot = filename.find_last_of( "." );
+            if( lastDot != std::string::npos )
+                filename = filename.substr( 0, lastDot );
+
+            tags.insert( "#" + filename );
+            setTags( test, tags );
+        }
+    }
 
     class Session : NonCopyable {
         static bool alreadyInstantiated;
@@ -5616,7 +6083,7 @@ namespace Catch {
             Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
         }
 
-        int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+        int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
             try {
                 m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
                 m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
@@ -5643,7 +6110,7 @@ namespace Catch {
             m_config.reset();
         }
 
-        int run( int argc, char* const argv[] ) {
+        int run( int argc, char const* const argv[] ) {
 
             int returnCode = applyCommandLine( argc, argv );
             if( returnCode == 0 )
@@ -5659,15 +6126,16 @@ namespace Catch {
             {
                 config(); // Force config to be constructed
 
-                std::srand( m_configData.rngSeed );
+                seedRng( *m_config );
 
-                Runner runner( m_config );
+                if( m_configData.filenamesAsTags )
+                    applyFilenamesAsTags( *m_config );
 
                 // Handle list request
                 if( Option<std::size_t> listed = list( config() ) )
                     return static_cast<int>( *listed );
 
-                return static_cast<int>( runner.runTests().assertions.failed );
+                return static_cast<int>( runTests( m_config ).assertions.failed );
             }
             catch( std::exception& ex ) {
                 Catch::cerr() << ex.what() << std::endl;
@@ -5689,7 +6157,6 @@ namespace Catch {
                 m_config = new Config( m_configData );
             return *m_config;
         }
-
     private:
         Clara::CommandLine<ConfigData> m_cli;
         std::vector<Clara::Parser::Token> m_unusedTokens;
@@ -5715,16 +6182,76 @@ namespace Catch {
 
 namespace Catch {
 
-    class TestRegistry : public ITestCaseRegistry {
-        struct LexSort {
-            bool operator() (TestCase i,TestCase j) const { return (i<j);}
-        };
-        struct RandomNumberGenerator {
-            int operator()( int n ) const { return std::rand() % n; }
-        };
+    struct LexSort {
+        bool operator() (TestCase i,TestCase j) const { return (i<j);}
+    };
+    struct RandomNumberGenerator {
+        int operator()( int n ) const { return std::rand() % n; }
+    };
+
+    inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+        std::vector<TestCase> sorted = unsortedTestCases;
+
+        switch( config.runOrder() ) {
+            case RunTests::InLexicographicalOrder:
+                std::sort( sorted.begin(), sorted.end(), LexSort() );
+                break;
+            case RunTests::InRandomOrder:
+                {
+                    seedRng( config );
+
+                    RandomNumberGenerator rng;
+                    std::random_shuffle( sorted.begin(), sorted.end(), rng );
+                }
+                break;
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
+        }
+        return sorted;
+    }
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+    }
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+            it != itEnd;
+            ++it ) {
+            std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+            if( !prev.second ){
+                Catch::cerr()
+                << Colour( Colour::Red )
+                << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+                << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+                << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+                exit(1);
+            }
+        }
+    }
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCase> filtered;
+        filtered.reserve( testCases.size() );
+        for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                it != itEnd;
+                ++it )
+            if( matchTest( *it, testSpec, config ) )
+                filtered.push_back( *it );
+        return filtered;
+    }
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
 
+    class TestRegistry : public ITestCaseRegistry {
     public:
-        TestRegistry() : m_unnamedCount( 0 ) {}
+        TestRegistry()
+        :   m_currentSortOrder( RunTests::InDeclarationOrder ),
+            m_unnamedCount( 0 )
+        {}
         virtual ~TestRegistry();
 
         virtual void registerTest( TestCase const& testCase ) {
@@ -5734,69 +6261,29 @@ namespace Catch {
                 oss << "Anonymous test case " << ++m_unnamedCount;
                 return registerTest( testCase.withName( oss.str() ) );
             }
-
-            if( m_functions.find( testCase ) == m_functions.end() ) {
-                m_functions.insert( testCase );
-                m_functionsInOrder.push_back( testCase );
-                if( !testCase.isHidden() )
-                    m_nonHiddenFunctions.push_back( testCase );
-            }
-            else {
-                TestCase const& prev = *m_functions.find( testCase );
-                {
-                    Colour colourGuard( Colour::Red );
-                    Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
-                                << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
-                                << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
-                }
-                exit(1);
-            }
+            m_functions.push_back( testCase );
         }
 
         virtual std::vector<TestCase> const& getAllTests() const {
-            return m_functionsInOrder;
+            return m_functions;
         }
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+            if( m_sortedFunctions.empty() )
+                enforceNoDuplicateTestCases( m_functions );
 
-        virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
-            return m_nonHiddenFunctions;
-        }
-
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
-
-            for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(),
-                                                        itEnd = m_functionsInOrder.end();
-                    it != itEnd;
-                    ++it ) {
-                bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
-                if( includeTest != negated )
-                    matchingTestCases.push_back( *it );
+            if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+                m_sortedFunctions = sortTests( config, m_functions );
+                m_currentSortOrder = config.runOrder();
             }
-            sortTests( config, matchingTestCases );
+            return m_sortedFunctions;
         }
 
     private:
-
-        static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
-
-            switch( config.runOrder() ) {
-                case RunTests::InLexicographicalOrder:
-                    std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
-                    break;
-                case RunTests::InRandomOrder:
-                {
-                    RandomNumberGenerator rng;
-                    std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
-                }
-                    break;
-                case RunTests::InDeclarationOrder:
-                    // already in declaration order
-                    break;
-            }
-        }
-        std::set<TestCase> m_functions;
-        std::vector<TestCase> m_functionsInOrder;
-        std::vector<TestCase> m_nonHiddenFunctions;
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
         size_t m_unnamedCount;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
     };
 
     ///////////////////////////////////////////////////////////////////////////
@@ -5829,29 +6316,38 @@ namespace Catch {
         return className;
     }
 
-    ///////////////////////////////////////////////////////////////////////////
+    void registerTestCase
+        (   ITestCase* testCase,
+            char const* classOrQualifiedMethodName,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
 
-    AutoReg::AutoReg(   TestFunction function,
-                        SourceLineInfo const& lineInfo,
-                        NameAndDesc const& nameAndDesc ) {
+        getMutableRegistryHub().registerTest
+            ( makeTestCase
+                (   testCase,
+                    extractClassName( classOrQualifiedMethodName ),
+                    nameAndDesc.name,
+                    nameAndDesc.description,
+                    lineInfo ) );
+    }
+    void registerTestCaseFunction
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
         registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
     }
 
-    AutoReg::~AutoReg() {}
-
-    void AutoReg::registerTestCase( ITestCase* testCase,
-                                    char const* classOrQualifiedMethodName,
-                                    NameAndDesc const& nameAndDesc,
-                                    SourceLineInfo const& lineInfo ) {
+    ///////////////////////////////////////////////////////////////////////////
 
-        getMutableRegistryHub().registerTest
-            ( makeTestCase( testCase,
-                            extractClassName( classOrQualifiedMethodName ),
-                            nameAndDesc.name,
-                            nameAndDesc.description,
-                            lineInfo ) );
+    AutoReg::AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCaseFunction( function, lineInfo, nameAndDesc );
     }
 
+    AutoReg::~AutoReg() {}
+
 } // end namespace Catch
 
 // #included from: catch_reporter_registry.hpp
@@ -5865,27 +6361,32 @@ namespace Catch {
 
     public:
 
-        virtual ~ReporterRegistry() {
-            deleteAllValues( m_factories );
-        }
+        virtual ~ReporterRegistry() CATCH_OVERRIDE {}
 
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
             FactoryMap::const_iterator it =  m_factories.find( name );
             if( it == m_factories.end() )
-                return NULL;
+                return CATCH_NULL;
             return it->second->create( ReporterConfig( config ) );
         }
 
-        void registerReporter( std::string const& name, IReporterFactory* factory ) {
+        void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
             m_factories.insert( std::make_pair( name, factory ) );
         }
+        void registerListener( Ptr<IReporterFactory> const& factory ) {
+            m_listeners.push_back( factory );
+        }
 
-        FactoryMap const& getFactories() const {
+        virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
             return m_factories;
         }
+        virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+            return m_listeners;
+        }
 
     private:
         FactoryMap m_factories;
+        Listeners m_listeners;
     };
 }
 
@@ -5913,13 +6414,13 @@ namespace Catch {
 #ifdef __OBJC__
                 // In Objective-C try objective-c exceptions first
                 @try {
-                    throw;
+                    return tryTranslators();
                 }
                 @catch (NSException *exception) {
                     return Catch::toString( [exception description] );
                 }
 #else
-                throw;
+                return tryTranslators();
 #endif
             }
             catch( TestFailureException& ) {
@@ -5935,20 +6436,15 @@ namespace Catch {
                 return msg;
             }
             catch(...) {
-                return tryTranslators( m_translators.begin() );
+                return "Unknown exception";
             }
         }
 
-        std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
-            if( it == m_translators.end() )
-                return "Unknown exception";
-
-            try {
-                return (*it)->translate();
-            }
-            catch(...) {
-                return tryTranslators( it+1 );
-            }
+        std::string tryTranslators() const {
+            if( m_translators.empty() )
+                throw;
+            else
+                return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
         }
 
     private:
@@ -5968,24 +6464,27 @@ namespace Catch {
         public: // IRegistryHub
             RegistryHub() {
             }
-            virtual IReporterRegistry const& getReporterRegistry() const {
+            virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
                 return m_reporterRegistry;
             }
-            virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
                 return m_testCaseRegistry;
             }
-            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
                 return m_exceptionTranslatorRegistry;
             }
 
         public: // IMutableRegistryHub
-            virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+            virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
                 m_reporterRegistry.registerReporter( name, factory );
             }
-            virtual void registerTest( TestCase const& testInfo ) {
+            virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerListener( factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
                 m_testCaseRegistry.registerTest( testInfo );
             }
-            virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
                 m_exceptionTranslatorRegistry.registerTranslator( translator );
             }
 
@@ -5997,7 +6496,7 @@ namespace Catch {
 
         // Single, global, instance
         inline RegistryHub*& getTheRegistryHub() {
-            static RegistryHub* theRegistryHub = NULL;
+            static RegistryHub* theRegistryHub = CATCH_NULL;
             if( !theRegistryHub )
                 theRegistryHub = new RegistryHub();
             return theRegistryHub;
@@ -6012,7 +6511,7 @@ namespace Catch {
     }
     void cleanUp() {
         delete getTheRegistryHub();
-        getTheRegistryHub() = NULL;
+        getTheRegistryHub() = CATCH_NULL;
         cleanUpContext();
     }
     std::string translateActiveException() {
@@ -6042,24 +6541,11 @@ namespace Catch {
 
 } // end namespace Catch
 
-// #included from: catch_context_impl.hpp
-#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
-
-// #included from: catch_stream.hpp
-#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
-
-// #included from: catch_streambuf.h
-#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
-
-#include <streambuf>
-
-namespace Catch {
-
-    class StreamBufBase : public std::streambuf {
-    public:
-        virtual ~StreamBufBase() CATCH_NOEXCEPT;
-    };
-}
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
 
 #include <stdexcept>
 #include <cstdio>
@@ -6105,6 +6591,19 @@ namespace Catch {
 
     ///////////////////////////////////////////////////////////////////////////
 
+    FileStream::FileStream( std::string const& filename ) {
+        m_ofs.open( filename.c_str() );
+        if( m_ofs.fail() ) {
+            std::ostringstream oss;
+            oss << "Unable to open file: '" << filename << "'";
+            throw std::domain_error( oss.str() );
+        }
+    }
+
+    std::ostream& FileStream::stream() const {
+        return m_ofs;
+    }
+
     struct OutputDebugWriter {
 
         void operator()( std::string const&str ) {
@@ -6112,23 +6611,26 @@ namespace Catch {
         }
     };
 
-    Stream::Stream()
-    : streamBuf( NULL ), isOwned( false )
+    DebugOutStream::DebugOutStream()
+    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+        m_os( m_streamBuf.get() )
     {}
 
-    Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
-    : streamBuf( _streamBuf ), isOwned( _isOwned )
+    std::ostream& DebugOutStream::stream() const {
+        return m_os;
+    }
+
+    // Store the streambuf from cout up-front because
+    // cout may get redirected when running tests
+    CoutStream::CoutStream()
+    :   m_os( Catch::cout().rdbuf() )
     {}
 
-    void Stream::release() {
-        if( isOwned ) {
-            delete streamBuf;
-            streamBuf = NULL;
-            isOwned = false;
-        }
+    std::ostream& CoutStream::stream() const {
+        return m_os;
     }
 
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
     std::ostream& cout() {
         return std::cout;
     }
@@ -6142,7 +6644,7 @@ namespace Catch {
 
     class Context : public IMutableContext {
 
-        Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+        Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
         Context( Context const& );
         void operator=( Context const& );
 
@@ -6188,7 +6690,7 @@ namespace Catch {
                 m_generatorsByTestName.find( testName );
             return it != m_generatorsByTestName.end()
                 ? it->second
-                : NULL;
+                : CATCH_NULL;
         }
 
         IGeneratorsForTest& getGeneratorsForCurrentTest() {
@@ -6209,7 +6711,7 @@ namespace Catch {
     };
 
     namespace {
-        Context* currentContext = NULL;
+        Context* currentContext = CATCH_NULL;
     }
     IMutableContext& getCurrentMutableContext() {
         if( !currentContext )
@@ -6220,17 +6722,9 @@ namespace Catch {
         return getCurrentMutableContext();
     }
 
-    Stream createStream( std::string const& streamName ) {
-        if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
-        if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
-        if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
-
-        throw std::domain_error( "Unknown stream: " + streamName );
-    }
-
     void cleanUpContext() {
         delete currentContext;
-        currentContext = NULL;
+        currentContext = CATCH_NULL;
     }
 }
 
@@ -6286,12 +6780,13 @@ namespace {
         {
             CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
             GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
-            originalAttributes = csbiInfo.wAttributes;
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
         }
 
         virtual void use( Colour::Code _colourCode ) {
             switch( _colourCode ) {
-                case Colour::None:      return setTextAttribute( originalAttributes );
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
                 case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
                 case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
@@ -6311,10 +6806,11 @@ namespace {
 
     private:
         void setTextAttribute( WORD _textAttribute ) {
-            SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
         }
         HANDLE stdoutHandle;
-        WORD originalAttributes;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
     };
 
     IColourImpl* platformColourInstance() {
@@ -6640,6 +7136,21 @@ namespace Catch {
         return TestCase( _testCase, info );
     }
 
+    void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+    {
+        testCaseInfo.tags = tags;
+        testCaseInfo.lcaseTags.clear();
+
+        std::ostringstream oss;
+        for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+            oss << "[" << *it << "]";
+            std::string lcaseTag = toLower( *it );
+            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+            testCaseInfo.lcaseTags.insert( lcaseTag );
+        }
+        testCaseInfo.tagsAsString = oss.str();
+    }
+
     TestCaseInfo::TestCaseInfo( std::string const& _name,
                                 std::string const& _className,
                                 std::string const& _description,
@@ -6648,18 +7159,10 @@ namespace Catch {
     :   name( _name ),
         className( _className ),
         description( _description ),
-        tags( _tags ),
         lineInfo( _lineInfo ),
         properties( None )
     {
-        std::ostringstream oss;
-        for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
-            oss << "[" << *it << "]";
-            std::string lcaseTag = toLower( *it );
-            properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
-            lcaseTags.insert( lcaseTag );
-        }
-        tagsAsString = oss.str();
+        setTags( *this, _tags );
     }
 
     TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
@@ -6767,7 +7270,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 2, 1, "", 0 );
+    Version libraryVersion( 1, 3, 3, "", 0 );
 
 }
 
@@ -6960,7 +7463,7 @@ namespace Catch {
 #else
         uint64_t getCurrentTicks() {
             timeval t;
-            gettimeofday(&t,NULL);
+            gettimeofday(&t,CATCH_NULL);
             return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
         }
 #endif
@@ -7059,6 +7562,14 @@ namespace Catch {
         return line < other.line || ( line == other.line  && file < other.file );
     }
 
+    void seedRng( IConfig const& config ) {
+        if( config.rngSeed() != 0 )
+            std::srand( config.rngSeed() );
+    }
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
     std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
 #ifndef __GNUG__
         os << info.file << "(" << info.line << ")";
@@ -7098,8 +7609,13 @@ namespace Catch {
     }
 
     Section::~Section() {
-        if( m_sectionIncluded )
-            getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+            if( std::uncaught_exception() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
     }
 
     // This indicates whether the section should be executed or not
@@ -7151,7 +7667,7 @@ namespace Catch {
             // Call sysctl.
 
             size = sizeof(info);
-            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
                 Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
                 return false;
             }
@@ -7205,9 +7721,11 @@ namespace Catch {
 
 namespace Detail {
 
-    std::string unprintableString = "{?}";
+    const std::string unprintableString = "{?}";
 
     namespace {
+        const int hexThreshold = 255;
+
         struct Endianness {
             enum Arch { Big, Little };
 
@@ -7289,7 +7807,7 @@ std::string toString( wchar_t* const value )
 std::string toString( int value ) {
     std::ostringstream oss;
     oss << value;
-    if( value >= 255 )
+    if( value > Detail::hexThreshold )
         oss << " (0x" << std::hex << value << ")";
     return oss.str();
 }
@@ -7297,7 +7815,7 @@ std::string toString( int value ) {
 std::string toString( unsigned long value ) {
     std::ostringstream oss;
     oss << value;
-    if( value >= 255 )
+    if( value > Detail::hexThreshold )
         oss << " (0x" << std::hex << value << ")";
     return oss.str();
 }
@@ -7347,6 +7865,23 @@ std::string toString( unsigned char value ) {
     return toString( static_cast<char>( value ) );
 }
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+std::string toString( unsigned long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+#endif
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
 std::string toString( std::nullptr_t ) {
     return "nullptr";
@@ -7376,11 +7911,17 @@ std::string toString( std::nullptr_t ) {
 
 namespace Catch {
 
+    std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) {
+        return secondArg.empty() || secondArg == "\"\""
+            ? capturedExpression
+            : capturedExpression + ", " + secondArg;
+    }
     ResultBuilder::ResultBuilder(   char const* macroName,
                                     SourceLineInfo const& lineInfo,
                                     char const* capturedExpression,
-                                    ResultDisposition::Flags resultDisposition )
-    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+                                    ResultDisposition::Flags resultDisposition,
+                                    char const* secondArg )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ),
         m_shouldDebugBreak( false ),
         m_shouldThrow( false )
     {}
@@ -7421,9 +7962,35 @@ namespace Catch {
         setResultType( resultType );
         captureExpression();
     }
+    void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+        if( expectedMessage.empty() )
+            captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() );
+        else
+            captureExpectedException( Matchers::Equals( expectedMessage ) );
+    }
+
+    void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) {
+
+        assert( m_exprComponents.testFalse == false );
+        AssertionResultData data = m_data;
+        data.resultType = ResultWas::Ok;
+        data.reconstructedExpression = m_assertionInfo.capturedExpression;
+
+        std::string actualMessage = Catch::translateActiveException();
+        if( !matcher.match( actualMessage ) ) {
+            data.resultType = ResultWas::ExpressionFailed;
+            data.reconstructedExpression = actualMessage;
+        }
+        AssertionResult result( m_assertionInfo, data );
+        handleResult( result );
+    }
 
     void ResultBuilder::captureExpression() {
         AssertionResult result = build();
+        handleResult( result );
+    }
+    void ResultBuilder::handleResult( AssertionResult const& result )
+    {
         getResultCapture().assertionEnded( result );
 
         if( !result.isOk() ) {
@@ -7576,6 +8143,137 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+    typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+    Reporters m_reporters;
+
+public:
+    void add( Ptr<IStreamingReporter> const& reporter ) {
+        m_reporters.push_back( reporter );
+    }
+
+public: // IStreamingReporter
+
+    virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+        return m_reporters[0]->getPreferences();
+    }
+
+    virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->noMatchingTestCases( spec );
+    }
+
+    virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunStarting( testRunInfo );
+    }
+
+    virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupStarting( groupInfo );
+    }
+
+    virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseStarting( testInfo );
+    }
+
+    virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionStarting( sectionInfo );
+    }
+
+    virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->assertionStarting( assertionInfo );
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+        bool clearBuffer = false;
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            clearBuffer |= (*it)->assertionEnded( assertionStats );
+        return clearBuffer;
+    }
+
+    virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionEnded( sectionStats );
+    }
+
+    virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseEnded( testCaseStats );
+    }
+
+    virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupEnded( testGroupStats );
+    }
+
+    virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunEnded( testRunStats );
+    }
+
+    virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->skipTest( testInfo );
+    }
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+    Ptr<IStreamingReporter> resultingReporter;
+
+    if( existingReporter ) {
+        MultipleReporters* multi = dynamic_cast<MultipleReporters*>( existingReporter.get() );
+        if( !multi ) {
+            multi = new MultipleReporters;
+            resultingReporter = Ptr<IStreamingReporter>( multi );
+            if( existingReporter )
+                multi->add( existingReporter );
+        }
+        else
+            resultingReporter = existingReporter;
+        multi->add( additionalReporter );
+    }
+    else
+        resultingReporter = additionalReporter;
+
+    return resultingReporter;
+}
+
+} // end namespace Catch
+
 // #included from: ../reporters/catch_reporter_xml.hpp
 #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
 
@@ -7591,47 +8289,53 @@ namespace Catch {
         StreamingReporterBase( ReporterConfig const& _config )
         :   m_config( _config.fullConfig() ),
             stream( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
 
-        virtual ~StreamingReporterBase();
+        virtual ~StreamingReporterBase() CATCH_OVERRIDE;
 
-        virtual void noMatchingTestCases( std::string const& ) {}
+        virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
 
-        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
             currentTestRunInfo = _testRunInfo;
         }
-        virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
             currentGroupInfo = _groupInfo;
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
             currentTestCaseInfo = _testInfo;
         }
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
             m_sectionStack.push_back( _sectionInfo );
         }
 
-        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
             m_sectionStack.pop_back();
         }
-        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
             currentTestCaseInfo.reset();
         }
-        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
             currentGroupInfo.reset();
         }
-        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
             currentTestCaseInfo.reset();
             currentGroupInfo.reset();
             currentTestRunInfo.reset();
         }
 
-        virtual void skipTest( TestCaseInfo const& ) {
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
             // Don't do anything with this by default.
             // It can optionally be overridden in the derived class.
         }
 
-        Ptr<IConfig> m_config;
+        Ptr<IConfig const> m_config;
         std::ostream& stream;
 
         LazyStat<TestRunInfo> currentTestRunInfo;
@@ -7639,6 +8343,7 @@ namespace Catch {
         LazyStat<TestCaseInfo> currentTestCaseInfo;
 
         std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
     };
 
     struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
@@ -7689,15 +8394,21 @@ namespace Catch {
         CumulativeReporterBase( ReporterConfig const& _config )
         :   m_config( _config.fullConfig() ),
             stream( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
         ~CumulativeReporterBase();
 
-        virtual void testRunStarting( TestRunInfo const& ) {}
-        virtual void testGroupStarting( GroupInfo const& ) {}
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+        virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
 
-        virtual void testCaseStarting( TestCaseInfo const& ) {}
+        virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
             SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
             Ptr<SectionNode> node;
             if( m_sectionStack.empty() ) {
@@ -7722,7 +8433,7 @@ namespace Catch {
             m_deepestSection = node;
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) {}
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
 
         virtual bool assertionEnded( AssertionStats const& assertionStats ) {
             assert( !m_sectionStack.empty() );
@@ -7730,13 +8441,13 @@ namespace Catch {
             sectionNode.assertions.push_back( assertionStats );
             return true;
         }
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
             assert( !m_sectionStack.empty() );
             SectionNode& node = *m_sectionStack.back();
             node.stats = sectionStats;
             m_sectionStack.pop_back();
         }
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
             assert( m_sectionStack.size() == 0 );
             node->children.push_back( m_rootSection );
@@ -7747,12 +8458,12 @@ namespace Catch {
             m_deepestSection->stdOut = testCaseStats.stdOut;
             m_deepestSection->stdErr = testCaseStats.stdErr;
         }
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
             node->children.swap( m_testCases );
             m_testGroups.push_back( node );
         }
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
             Ptr<TestRunNode> node = new TestRunNode( testRunStats );
             node->children.swap( m_testGroups );
             m_testRuns.push_back( node );
@@ -7760,9 +8471,9 @@ namespace Catch {
         }
         virtual void testRunEndedCumulative() = 0;
 
-        virtual void skipTest( TestCaseInfo const& ) {}
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
 
-        Ptr<IConfig> m_config;
+        Ptr<IConfig const> m_config;
         std::ostream& stream;
         std::vector<AssertionStats> m_assertions;
         std::vector<std::vector<Ptr<SectionNode> > > m_sections;
@@ -7774,6 +8485,7 @@ namespace Catch {
         Ptr<SectionNode> m_rootSection;
         Ptr<SectionNode> m_deepestSection;
         std::vector<Ptr<SectionNode> > m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
 
     };
 
@@ -7787,6 +8499,17 @@ namespace Catch {
         return line;
     }
 
+    struct TestEventListenerBase : StreamingReporterBase {
+        TestEventListenerBase( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config )
+        {}
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+        virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+            return false;
+        }
+    };
+
 } // end namespace Catch
 
 // #included from: ../internal/catch_reporter_registrars.hpp
@@ -7817,7 +8540,7 @@ namespace Catch {
     template<typename T>
     class ReporterRegistrar {
 
-        class ReporterFactory : public IReporterFactory {
+        class ReporterFactory : public SharedImpl<IReporterFactory> {
 
             // *** Please Note ***:
             // - If you end up here looking at a compiler error because it's trying to register
@@ -7845,22 +8568,102 @@ namespace Catch {
             getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
         }
     };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+            virtual std::string getDescription() const {
+                return "";
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( new ListenerFactory() );
+        }
+    };
 }
 
 #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
     namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
 #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
     namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
 
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
 // #included from: ../internal/catch_xmlwriter.hpp
 #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
 
 #include <sstream>
 #include <string>
 #include <vector>
+#include <iomanip>
 
 namespace Catch {
 
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+        :   m_str( str ),
+            m_forWhat( forWhat )
+        {}
+
+        void encodeTo( std::ostream& os ) const {
+
+            // Apostrophe escaping not necessary if we always use " to write attributes
+            // (see: http://www.w3.org/TR/xml/#syntax)
+
+            for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+                char c = m_str[i];
+                switch( c ) {
+                    case '<':   os << "<"; break;
+                    case '&':   os << "&"; break;
+
+                    case '>':
+                        // See: http://www.w3.org/TR/xml/#syntax
+                        if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+                            os << ">";
+                        else
+                            os << c;
+                        break;
+
+                    case '\"':
+                        if( m_forWhat == ForAttributes )
+                            os << """;
+                        else
+                            os << c;
+                        break;
+
+                    default:
+                        // Escape control chars - based on contribution by @espenalb in PR #465
+                        if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
+                            os << "&#x" << std::uppercase << std::hex << static_cast<int>( c );
+                        else
+                            os << c;
+                }
+            }
+        }
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+            xmlEncode.encodeTo( os );
+            return os;
+        }
+
+    private:
+        std::string m_str;
+        ForWhat m_forWhat;
+    };
+
     class XmlWriter {
     public:
 
@@ -7872,7 +8675,7 @@ namespace Catch {
 
             ScopedElement( ScopedElement const& other )
             :   m_writer( other.m_writer ){
-                other.m_writer = NULL;
+                other.m_writer = CATCH_NULL;
             }
 
             ~ScopedElement() {
@@ -7943,11 +8746,8 @@ namespace Catch {
         }
 
         XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
-            if( !name.empty() && !attribute.empty() ) {
-                stream() << " " << name << "=\"";
-                writeEncodedText( attribute );
-                stream() << "\"";
-            }
+            if( !name.empty() && !attribute.empty() )
+                stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\"";
             return *this;
         }
 
@@ -7958,9 +8758,9 @@ namespace Catch {
 
         template<typename T>
         XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
-            if( !name.empty() )
-                stream() << " " << name << "=\"" << attribute << "\"";
-            return *this;
+            std::ostringstream oss;
+            oss << attribute;
+            return writeAttribute( name, oss.str() );
         }
 
         XmlWriter& writeText( std::string const& text, bool indent = true ) {
@@ -7969,7 +8769,7 @@ namespace Catch {
                 ensureTagClosed();
                 if( tagWasOpen && indent )
                     stream() << m_indent;
-                writeEncodedText( text );
+                stream() << XmlEncode( text );
                 m_needsNewline = true;
             }
             return *this;
@@ -8014,30 +8814,6 @@ namespace Catch {
             }
         }
 
-        void writeEncodedText( std::string const& text ) {
-            static const char* charsToEncode = "<&\"";
-            std::string mtext = text;
-            std::string::size_type pos = mtext.find_first_of( charsToEncode );
-            while( pos != std::string::npos ) {
-                stream() << mtext.substr( 0, pos );
-
-                switch( mtext[pos] ) {
-                    case '<':
-                        stream() << "<";
-                        break;
-                    case '&':
-                        stream() << "&";
-                        break;
-                    case '\"':
-                        stream() << """;
-                        break;
-                }
-                mtext = mtext.substr( pos+1 );
-                pos = mtext.find_first_of( charsToEncode );
-            }
-            stream() << mtext;
-        }
-
         bool m_tagIsOpen;
         bool m_needsNewline;
         std::vector<std::string> m_tags;
@@ -8046,32 +8822,44 @@ namespace Catch {
     };
 
 }
+// #included from: catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+
 namespace Catch {
     class XmlReporter : public StreamingReporterBase {
     public:
         XmlReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config ),
             m_sectionDepth( 0 )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
 
-        virtual ~XmlReporter();
+        virtual ~XmlReporter() CATCH_OVERRIDE;
 
         static std::string getDescription() {
             return "Reports test results as an XML document";
         }
 
     public: // StreamingReporterBase
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
 
-        virtual void noMatchingTestCases( std::string const& s ) {
+        virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
             StreamingReporterBase::noMatchingTestCases( s );
         }
 
-        virtual void testRunStarting( TestRunInfo const& testInfo ) {
+        virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunStarting( testInfo );
             m_xml.setStream( stream );
             m_xml.startElement( "Catch" );
@@ -8079,13 +8867,13 @@ namespace Catch {
                 m_xml.writeAttribute( "name", m_config->name() );
         }
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testGroupStarting( groupInfo );
             m_xml.startElement( "Group" )
                 .writeAttribute( "name", groupInfo.name );
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseStarting(testInfo);
             m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
 
@@ -8093,7 +8881,7 @@ namespace Catch {
                 m_testCaseTimer.start();
         }
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::sectionStarting( sectionInfo );
             if( m_sectionDepth++ > 0 ) {
                 m_xml.startElement( "Section" )
@@ -8102,9 +8890,9 @@ namespace Catch {
             }
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) { }
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             const AssertionResult& assertionResult = assertionStats.assertionResult;
 
             // Print any info messages in <Info> tags.
@@ -8175,7 +8963,7 @@ namespace Catch {
             return true;
         }
 
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
             StreamingReporterBase::sectionEnded( sectionStats );
             if( --m_sectionDepth > 0 ) {
                 XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
@@ -8190,7 +8978,7 @@ namespace Catch {
             }
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseEnded( testCaseStats );
             XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
             e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
@@ -8201,7 +8989,7 @@ namespace Catch {
             m_xml.endElement();
         }
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testGroupEnded( testGroupStats );
             // TODO: Check testGroupStats.aborting and act accordingly.
             m_xml.scopedElement( "OverallResults" )
@@ -8211,7 +8999,7 @@ namespace Catch {
             m_xml.endElement();
         }
 
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunEnded( testRunStats );
             m_xml.scopedElement( "OverallResults" )
                 .writeAttribute( "successes", testRunStats.totals.assertions.passed )
@@ -8242,28 +9030,24 @@ namespace Catch {
         JunitReporter( ReporterConfig const& _config )
         :   CumulativeReporterBase( _config ),
             xml( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
 
-        ~JunitReporter();
+        virtual ~JunitReporter() CATCH_OVERRIDE;
 
         static std::string getDescription() {
             return "Reports test results in an XML format that looks like Ant's junitreport target";
         }
 
-        virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
-
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
 
-        virtual void testRunStarting( TestRunInfo const& runInfo ) {
+        virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
             CumulativeReporterBase::testRunStarting( runInfo );
             xml.startElement( "testsuites" );
         }
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
             suiteTimer.start();
             stdOutForSuite.str("");
             stdErrForSuite.str("");
@@ -8271,25 +9055,25 @@ namespace Catch {
             CumulativeReporterBase::testGroupStarting( groupInfo );
         }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
                 unexpectedExceptions++;
             return CumulativeReporterBase::assertionEnded( assertionStats );
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             stdOutForSuite << testCaseStats.stdOut;
             stdErrForSuite << testCaseStats.stdErr;
             CumulativeReporterBase::testCaseEnded( testCaseStats );
         }
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             double suiteTime = suiteTimer.getElapsedSeconds();
             CumulativeReporterBase::testGroupEnded( testGroupStats );
             writeGroup( *m_testGroups.back(), suiteTime );
         }
 
-        virtual void testRunEndedCumulative() {
+        virtual void testRunEndedCumulative() CATCH_OVERRIDE {
             xml.endElement();
         }
 
@@ -8454,24 +9238,19 @@ namespace Catch {
             m_headerPrinted( false )
         {}
 
-        virtual ~ConsoleReporter();
+        virtual ~ConsoleReporter() CATCH_OVERRIDE;
         static std::string getDescription() {
             return "Reports test results as plain lines of text";
         }
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = false;
-            return prefs;
-        }
 
-        virtual void noMatchingTestCases( std::string const& spec ) {
+        virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
             stream << "No test cases matched '" << spec << "'" << std::endl;
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) {
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
         }
 
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
             AssertionResult const& result = _assertionStats.assertionResult;
 
             bool printInfoMessages = true;
@@ -8491,11 +9270,11 @@ namespace Catch {
             return true;
         }
 
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
             m_headerPrinted = false;
             StreamingReporterBase::sectionStarting( _sectionInfo );
         }
-        virtual void sectionEnded( SectionStats const& _sectionStats ) {
+        virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
             if( _sectionStats.missingAssertions ) {
                 lazyPrint();
                 Colour colour( Colour::ResultError );
@@ -8517,11 +9296,11 @@ namespace Catch {
             StreamingReporterBase::sectionEnded( _sectionStats );
         }
 
-        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseEnded( _testCaseStats );
             m_headerPrinted = false;
         }
-        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
             if( currentGroupInfo.used ) {
                 printSummaryDivider();
                 stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
@@ -8530,7 +9309,7 @@ namespace Catch {
             }
             StreamingReporterBase::testGroupEnded( _testGroupStats );
         }
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
             printTotalsDivider( _testRunStats.totals );
             printTotals( _testRunStats.totals );
             stream << std::endl;
@@ -9169,8 +9948,14 @@ namespace Catch {
 } // end namespace Catch
 
 namespace Catch {
+    // These are all here to avoid warnings about not having any out of line
+    // virtual methods
     NonCopyable::~NonCopyable() {}
     IShared::~IShared() {}
+    IStream::~IStream() CATCH_NOEXCEPT {}
+    FileStream::~FileStream() CATCH_NOEXCEPT {}
+    CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+    DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
     StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
     IContext::~IContext() {}
     IResultCapture::~IResultCapture() {}
@@ -9204,6 +9989,7 @@ namespace Catch {
     FreeFunctionTestCase::~FreeFunctionTestCase() {}
     IGeneratorInfo::~IGeneratorInfo() {}
     IGeneratorsForTest::~IGeneratorsForTest() {}
+    WildcardPattern::~WildcardPattern() {}
     TestSpec::Pattern::~Pattern() {}
     TestSpec::NamePattern::~NamePattern() {}
     TestSpec::TagPattern::~TagPattern() {}
@@ -9215,6 +10001,13 @@ namespace Catch {
     Matchers::Impl::StdString::EndsWith::~EndsWith() {}
 
     void Config::dummy() {}
+
+    namespace TestCaseTracking {
+        ITracker::~ITracker() {}
+        TrackerBase::~TrackerBase() {}
+        SectionTracker::~SectionTracker() {}
+        IndexTracker::~IndexTracker() {}
+    }
 }
 
 #ifdef __clang__
@@ -9230,7 +10023,7 @@ namespace Catch {
 #ifndef __OBJC__
 
 // Standard C/C++ main entry point
-int main (int argc, char * const argv[]) {
+int main (int argc, char * argv[]) {
     return Catch::Session().run( argc, argv );
 }
 
@@ -9268,8 +10061,9 @@ int main (int argc, char * const argv[]) {
 #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
 #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
 
-#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" )
 #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" )
 #define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
 
 #define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
@@ -9280,6 +10074,7 @@ int main (int argc, char * const argv[]) {
 
 #define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
 #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" )
 #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
 
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
@@ -9295,6 +10090,7 @@ int main (int argc, char * const argv[]) {
     #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
     #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
     #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
     #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
     #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
     #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
@@ -9302,6 +10098,7 @@ int main (int argc, char * const argv[]) {
     #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
     #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
     #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
     #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
     #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
     #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
@@ -9321,11 +10118,11 @@ int main (int argc, char * const argv[]) {
 #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
 #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
 #endif
-#define CATCH_GIVEN( desc )    CATCH_SECTION( "Given: " desc, "" )
-#define CATCH_WHEN( desc )     CATCH_SECTION( " When: " desc, "" )
-#define CATCH_AND_WHEN( desc ) CATCH_SECTION( "  And: " desc, "" )
-#define CATCH_THEN( desc )     CATCH_SECTION( " Then: " desc, "" )
-#define CATCH_AND_THEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
 
 // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
 #else
@@ -9333,8 +10130,9 @@ int main (int argc, char * const argv[]) {
 #define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
 #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
 
-#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" )
 #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" )
 #define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
 
 #define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
@@ -9343,8 +10141,9 @@ int main (int argc, char * const argv[]) {
 #define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
 #define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
 
-#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" )
 #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" )
 #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
 
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
@@ -9360,6 +10159,7 @@ int main (int argc, char * const argv[]) {
     #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
     #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
     #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
     #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
     #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
     #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
@@ -9367,6 +10167,7 @@ int main (int argc, char * const argv[]) {
     #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
     #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
     #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
     #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
     #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
     #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
@@ -9390,27 +10191,13 @@ int main (int argc, char * const argv[]) {
 #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
 #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
 #endif
-#define GIVEN( desc )    SECTION( "   Given: " desc, "" )
-#define WHEN( desc )     SECTION( "    When: " desc, "" )
-#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
-#define THEN( desc )     SECTION( "    Then: " desc, "" )
-#define AND_THEN( desc ) SECTION( "     And: " desc, "" )
+#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc, "" )
+#define WHEN( desc )     SECTION( std::string("    When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc )     SECTION( std::string("    Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc, "" )
 
 using Catch::Detail::Approx;
 
-// #included from: internal/catch_reenable_warnings.h
-
-#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-#    ifdef __ICC // icpc defines the __clang__ macro
-#        pragma warning(pop)
-#    else
-#        pragma clang diagnostic pop
-#    endif
-#elif defined __GNUC__
-#    pragma GCC diagnostic pop
-#endif
-
 #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
 
diff --git a/test/misc/CMakeLists.txt b/test/misc/CMakeLists.txt
index 4b26174..592c7c6 100644
--- a/test/misc/CMakeLists.txt
+++ b/test/misc/CMakeLists.txt
@@ -9,7 +9,7 @@
 do_test(version1 "osmium --version" "^osmium version ${OSMIUM_VERSION}\n")
 do_test(version2 "osmium version"   "^osmium version ${OSMIUM_VERSION}\n")
 
-do_test(unknown_command "osmium x" "^Unknown command 'x'. Try 'osmium help'.\n")
+do_test(unknown_command "osmium x" "^Unknown command or option 'x'. Try 'osmium help'.\n")
 
 
 #-----------------------------------------------------------------------------
diff --git a/test/check-refs/nw-okay.osm b/test/order/fail-order-n.osm
similarity index 55%
copy from test/check-refs/nw-okay.osm
copy to test/order/fail-order-n.osm
index 2ae8a8b..8c1d280 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/order/fail-order-n.osm
@@ -1,15 +1,6 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="testdata" upload="false">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
-  <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
-  <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <nd ref="10"/>
-    <nd ref="11"/>
-    <nd ref="12"/>
-  </way>
-  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="10" role=""/>
-    <member type="way" ref="21" role=""/>
-  </relation>
+  <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
 </osm>
diff --git a/test/check-refs/nw-okay.osm b/test/order/fail-order-r.osm
similarity index 71%
copy from test/check-refs/nw-okay.osm
copy to test/order/fail-order-r.osm
index 2ae8a8b..95b3238 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/order/fail-order-r.osm
@@ -6,8 +6,15 @@
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
+  </way>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
+    <nd ref="11"/>
   </way>
+  <relation id="31" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <member type="node" ref="10" role=""/>
+    <member type="way" ref="20" role=""/>
+  </relation>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <member type="node" ref="10" role=""/>
     <member type="way" ref="21" role=""/>
diff --git a/test/check-refs/nwr-okay.osm b/test/order/fail-order-rw.osm
similarity index 86%
rename from test/check-refs/nwr-okay.osm
rename to test/order/fail-order-rw.osm
index 2123439..5a0038a 100644
--- a/test/check-refs/nwr-okay.osm
+++ b/test/order/fail-order-rw.osm
@@ -6,10 +6,13 @@
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
-    <nd ref="12"/>
   </way>
   <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <member type="node" ref="10" role=""/>
     <member type="way" ref="20" role=""/>
   </relation>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
 </osm>
diff --git a/test/check-refs/nw-okay.osm b/test/order/fail-order-w.osm
similarity index 75%
copy from test/check-refs/nw-okay.osm
copy to test/order/fail-order-w.osm
index 2ae8a8b..92c4506 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/order/fail-order-w.osm
@@ -3,13 +3,12 @@
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
+    <nd ref="12"/>
+    <nd ref="11"/>
+  </way>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
-    <nd ref="12"/>
   </way>
-  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="10" role=""/>
-    <member type="way" ref="21" role=""/>
-  </relation>
 </osm>
diff --git a/test/check-refs/nw-okay.osm b/test/order/fail-order-wn.osm
similarity index 75%
rename from test/check-refs/nw-okay.osm
rename to test/order/fail-order-wn.osm
index 2ae8a8b..8fb6e20 100644
--- a/test/check-refs/nw-okay.osm
+++ b/test/order/fail-order-wn.osm
@@ -2,14 +2,13 @@
 <osm version="0.6" generator="testdata" upload="false">
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
-  <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
   <way id="20" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="10"/>
     <nd ref="11"/>
+  </way>
+  <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
+  <way id="21" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
     <nd ref="12"/>
+    <nd ref="11"/>
   </way>
-  <relation id="30" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1">
-    <member type="node" ref="10" role=""/>
-    <member type="way" ref="21" role=""/>
-  </relation>
 </osm>
diff --git a/test/renumber/CMakeLists.txt b/test/renumber/CMakeLists.txt
index 78fa435..4d639c0 100644
--- a/test/renumber/CMakeLists.txt
+++ b/test/renumber/CMakeLists.txt
@@ -12,14 +12,12 @@ endfunction()
 
 function(check_renumber2 _name _in1 _in2 _out)
     set(_idxdir "${PROJECT_BINARY_DIR}/test/renumber/index")
-    file(REMOVE_RECURSE ${_idxdir})
-    file(MAKE_DIRECTORY ${_idxdir})
     if(WIN32)
         set(_devnull "nul")
     else()
         set(_devnull "/dev/null")
     endif()
-    check_output2(renumber ${_name}
+    check_output2(renumber ${_name} ${_idxdir}
                   "renumber --index-directory=${_idxdir} --generator=test -f osm --overwrite -o ${_devnull} renumber/${_in1}"
                   "renumber --index-directory=${_idxdir} --generator=test -f osc renumber/${_in2}"
                   "renumber/${_out}"
@@ -31,3 +29,23 @@ check_renumber(sorted input-sorted.osm output-sorted.osm)
 check_renumber2(change input-sorted.osm input-change.osc output-change.osc)
 
 #-----------------------------------------------------------------------------
+
+# input data not ordered properly
+
+add_test(NAME renumber-fail-order-n COMMAND osmium renumber ${CMAKE_SOURCE_DIR}/test/order/fail-order-n.osm -f osm)
+set_tests_properties(renumber-fail-order-n PROPERTIES WILL_FAIL true)
+
+add_test(NAME renumber-fail-order-w COMMAND osmium renumber ${CMAKE_SOURCE_DIR}/test/order/fail-order-w.osm -f osm)
+set_tests_properties(renumber-fail-order-w PROPERTIES WILL_FAIL true)
+
+add_test(NAME renumber-fail-order-r COMMAND osmium renumber ${CMAKE_SOURCE_DIR}/test/order/fail-order-r.osm -f osm)
+set_tests_properties(renumber-fail-order-r PROPERTIES WILL_FAIL true)
+
+add_test(NAME renumber-fail-order-wn COMMAND osmium renumber ${CMAKE_SOURCE_DIR}/test/order/fail-order-wn.osm -f osm)
+set_tests_properties(renumber-fail-order-wn PROPERTIES WILL_FAIL true)
+
+add_test(NAME renumber-fail-order-rw COMMAND osmium renumber ${CMAKE_SOURCE_DIR}/test/order/fail-order-rw.osm -f osm)
+set_tests_properties(renumber-fail-order-rw PROPERTIES WILL_FAIL true)
+
+
+#-----------------------------------------------------------------------------
diff --git a/test/sort/output-bounds.osm b/test/sort/output-bounds.osm
index 6637420..1f3d517 100644
--- a/test/sort/output-bounds.osm
+++ b/test/sort/output-bounds.osm
@@ -1,6 +1,6 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <osm version="0.6" generator="test">
-  <bounds minlon="0.0000000" minlat="0.0000000" maxlon="15.0000000" maxlat="15.0000000"/>
+  <bounds minlat="0" minlon="0" maxlat="15" maxlon="15"/>
   <node id="10" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="1" lon="1"/>
   <node id="11" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="2" lon="1"/>
   <node id="12" version="1" timestamp="2015-01-01T01:00:00Z" uid="1" user="test" changeset="1" lat="3" lon="1"/>
diff --git a/zsh_completion/_osmium b/zsh_completion/_osmium
index f6552d1..5a97207 100644
--- a/zsh_completion/_osmium
+++ b/zsh_completion/_osmium
@@ -17,7 +17,7 @@ osmium_file_glob="'*.(osm|osh|osc|o5m|o5c|pbf|osm.pbf) *.(osm|osh|osc|o5m|o5c).(
 
 _osmium() {
     local -a osmium_commands
-    osmium_commands=(apply-changes cat changeset-filter check-refs fileinfo getid help merge-changes renumber sort time-filter)
+    osmium_commands=(add-locations-to-ways apply-changes cat changeset-filter check-refs fileinfo getid help merge-changes renumber show sort time-filter)
     if (( CURRENT > 2 )); then
         # Remember the subcommand name
         local cmd=${words[2]}
@@ -34,6 +34,8 @@ _osmium() {
 }
 
 _osmium-common-options() {
+    echo '(--help)-h[show usage help]'
+    echo '(-h)--help[show usage help]'
     echo '(--verbose)-v[set verbose mode]'
     echo '(-v)--verbose[set verbose mode]'
 }
@@ -60,6 +62,19 @@ _osmium-output-options() {
     echo '(-O)--overwrite[allow overwriting of existing output file]'
 }
 
+_osmium-add-locations-to-ways() {
+    _arguments : \
+        ${(f)"$(_osmium-common-options)"} \
+        ${(f)"$(_osmium-multiple-inputs-options)"} \
+        ${(f)"$(_osmium-output-options)"} \
+        '(--index-type -I --show-index-types)-i[set index type]:index_types:_osmium_index_types' \
+        '(-i -I --show-index-types)--index-type[set index type]:index_types:_osmium_index_types' \
+        '(--show-index-types -i --index-type -n --keep-untagged-nodes)-I[show available index types]' \
+        '(-I -i --index-type -n --keep-untagged-nodes)--show-index-types[show available index types]' \
+        '(--keep-untagged-nodes -I --show-index-types)-n[keep untagged nodes in output]' \
+        '(-n -I --show-index-types)--keep-untagged-nodes[keep untagged nodes in output]'
+}
+
 _osmium-apply-changes() {
     _arguments : \
         ${(f)"$(_osmium-common-options)"} \
@@ -134,7 +149,16 @@ _osmium-getid() {
         ${(f)"$(_osmium-common-options)"} \
         ${(f)"$(_osmium-single-input-options)"} \
         ${(f)"$(_osmium-output-options)"} \
-        "*:IDs (format\: [nwr]NUM):"
+        '--default-type[default item type]' \
+        '(--id-file)-i[read OSM IDs from text file]' \
+        '(-i)--id-file[read OSM IDs from text file]' \
+        '(--id-osm-file)-I[read OSM IDs from OSM file]' \
+        '(-I)--id-osm-file[read OSM IDs from OSM file]' \
+        '(--add-referenced)-r[recursively add referenced objects]' \
+        '(-r)--add-referenced[recursively add referenced objects]' \
+        '(--with-history)-H[make it work with history files]' \
+        '(-H)--with-history[make it work with history files]' \
+        "*:IDs (format\: [nwr]ID):"
 }
 
 _osmium-merge-changes() {
@@ -155,6 +179,23 @@ _osmium-renumber() {
         '(-i)--index-directory[read/write index files in this directory]:directory:_path_files -/'
 }
 
+_osmium-show() {
+    _arguments : \
+        ${(f)"$(_osmium-common-options)"} \
+        ${(f)"$(_osmium-single-input-options)"} \
+        '(--output-format --format-debug -d --format-opl -o --format-xml -x)-f[format of output OSM file]:OSM file format:_osmium_file_formats' \
+        '(-f --format-debug -d --format-opl -o --format-xml -x)--output-format[format of output OSM file]:OSM file format:_osmium_file_formats' \
+        '(--output-format -f -d --format-opl -o --format-xml -x)--format-debug[set format of output OSM file to debug]' \
+        '(--output-format -f --format-debug --format-opl -o --format-xml -x)-d[set format of output OSM file to debug]' \
+        '(--output-format -f --format-debug -d -o --format-xml -x)--format-opl[set format of output OSM file to OPL]' \
+        '(--output-format -f --format-debug -d --format-opl --format-xml -x)-o[set format of output OSM file to OPL]' \
+        '(--output-format -f --format-debug -d --format-opl -o -x)--format-xml[set format of output OSM file to XML]' \
+        '(--output-format -f --format-debug -d --format-opl -o --format-xml)-x[set format of output OSM file to XML]' \
+        '--no-pager[disable pager]' \
+        '*-t[read only object of given output types]:OSM entity type:_osmium_entity_type' \
+        '*--object-type[read only object of given output types]:OSM entity type:_osmium_entity_type'
+}
+
 _osmium-sort() {
     _arguments : \
         ${(f)"$(_osmium-common-options)"} \
@@ -197,11 +238,15 @@ _osmium_fileinfo_variables() {
     _values 'variable' $(osmium fileinfo --show-variables)
 }
 
+_osmium_index_types() {
+    _values 'index-types' $(osmium add-locations-to-ways --show-index-types)
+}
+
 _osmium-help() {
     local -a osmium_help_topics
-    osmium_help_topics=(apply-changes cat changeset-filter check-refs fileinfo getid help merge-changes renumber sort time-filter file-formats)
+    osmium_help_topics=(add-locations-to-ways apply-changes cat changeset-filter check-refs fileinfo getid help merge-changes renumber show sort time-filter file-formats)
     _describe -t osmium-help-topics 'osmium help topics' osmium_help_topics
 }
 
-_osmium "$@"
+#_osmium "$@"
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osmium-tool.git



More information about the Pkg-grass-devel mailing list